详尽笔记,用于复习和速查
将包含基础、进阶、高阶
目前更新到数据结构-JCF-Set
概述
Java SE(Standard Edition) 面向PC级应用开发
Java EE(Enterprise Edition) 面向企业级应用开发
Java ME(Micro Edition) 面向嵌入式应用开发
环境搭建
JDK下载
搜索java archive,选择版本下载。(本文选择JDK1.8)
安装JDK时,会同时安装JRE。
配置环境变量
新增
JAVA_HOME
C:\Java\jdk1.8.0_73
(jdk地址)
修改Path
新增%JAVA_HOME%\bin
上移到最高
打开cmd
java -version
有版本号输出
javac
有用法输出
即可。
配置Eclipse
修改字号
Window-Preferences-General-Appaerance-Colors and Fonts-右侧Java-Java Editor Text Font
显示行号
General-Editors-Text Editors-右侧show line numbers勾上
自动保存时间间隔
General-Workspace-右侧Workspace save interval(in minutes)(默认5分钟)
下方可以修改文本编码格式
复原视窗
Window-Perspective-Reset Perspective…
Eclipse使用教程
Java从编写到运行
编写:利用记事本/vi/IDE等完成代码文件(.java)编写
编译:利用JDK中javac.exe将代码(.java)编译成字节码文件(.class)
运行:java.exe读入并解释字节码文件(.class),最终在JVM上运行
编程标准
类名首字母大写,后面单词驼峰命名
方法名和变量名首字母小写,后面单词驼峰命名
阿里巴巴编程标准
基础知识
class类
java文件必须以.java作为扩展名
一个java文件只能有一个public class
public class的名字必须和文件名字一样,大小写都要完全一致
一个文件可有多个class,但是只能有一个是public。不提倡一个文件里面放多个类(内部类除外)
System.out.print 输出
System.out.println 换行输出
类是Java中最基础逻辑单位
Java所有内容都需放在类的范围中
类的构成
成员变量/属性
成员方法/函数
main函数
一个class最多只能有一个main函数**(经指正,一个类可以有多个main函数-通过重载,但只能有一个入口函数)**
类可以没有main函数,没有main函数的类不能主动执行,但可以被动执行(被调用执行)
程序入口都是main函数
main函数的形参和前缀修饰符public、static、void等都不能省略,是固定写法
注意区别
1 2 3 4 5 6 7 8 public static void main (String[] args) { } public void main () { }
严格意义上说,main函数不属于这个类的所拥有函数。
只是因为Java要求所有内容都必须放置在class范围内,所有main函数“寄居”在某一个class中。
main函数无法被其它方法/类所调用。
一个Java程序可调用多个其它Java class。
String[] args是main函数的形参,即在main函数中可使用args的值(这些值在main函数启动时输入)。
右键test.java,Run As->Run Configuration-右侧Arguments-输入Program arguments程序参数-Run
多个变量需用空格隔开
八种基本类型
boolean 布尔
true false(默认)
byte 字节
1 byte = 8 bits
存储有符号的,以二进制补码表示的整数
最小值-128,最大值127,默认值0
该类型用在大型数组中可显著节约空间(占用空间只用int类型的四分之一),主要代替小整数
在二进制文件读写中使用较多
1 2 3 4 byte a = (byte ) -129 ;System.out.println(a); byte b = (byte ) 128 ;System.out.println(b);
怎么理解:
标准值:-128~127
两边可以延伸
左延伸-384(-128)~-129(127)
右延伸128(-128)~383(127)
short/int/long 短整数/整数/长整数
short 16位 2个字节 -3276832767,-2^15 2^15-1,默认值0
int 32位,4个字节 -21474836482147483647,-2^31 2^31-1,默认值0
long 64位 8个字节 -92233720368547758089223372036854775807,-2^63 2^63-1,默认值0L
float/double 浮点数
float 单精度 32位 4个字节 1.410-45~3.4*10 38 默认值0.0f
double 双精度 64位 8个字节 4.9 10-324~1.7*10 308 默认值0.0d
float和double都不能用来表示很精确的数字
char 字符
16位 Unicode字符
最小值是 \u0000
(即为0)
最大值是\uffff
(即为65535)
(\u4e00
~\u9fa5
两万多汉字)
运算符
>>
右移 除以2
<<
左移 乘以2
自定义函数
重载(overload):同一个类中,函数名称可以相同,但函数参数的个数或者类型必须有所不同
不能以返回值来区分同名的函数
面向对象
对象=属性+方法
对象的规范=属性定义+方法定义
obj可看作是内存中一个对象(包含若干个数据)的句柄
在C/C++中,obj称为指针,在Java中称为Reference
对象赋值是Reference赋值,基本类型是直接值拷贝
理解:基本类型的变量值小,可直接拷贝;对象包含多个值,不容易复制,赋值采用共享同一块内存区域。
函数内的局部变量,编译器不会给默认值,需初始化;
类的成员变量,编译器会给默认值,可直接使用。
构造函数
构造函数名称必须和类名一样,且没有返回值
public void A(int x)不是构造函数,而是普通函数。
Java有构造函数,但没有析构函数
每个变量都有生命周期,它只能存储在离它最近的一对{}中**(经指正,此条不够严谨)**
Java具有内存自动回收机制,当变量退出其生命周期后,JVM会自动回收所分配的对象的内存。
对象回收效率依赖于垃圾回收器GC(Garbage Collector),其回收算法关系到性能好坏,是JVM研究热点。
每个Java类都必须有构造函数
若没有显式定义构造函数,Java编译器自动为该类产生一个空的无形参构造函数。若有显式构造函数,编译器就不会再产生构造函数
每个子类的构造函数的第一句话,都默认调用父类的无参数构造函数super(),除非子类的构造函数第一句话是super,而且super语句必须放在第一条
一个类可有多个构造函数,只要形参列表不同
多个class可写在一个.java文件中,但最多只有一个类是public class,并且public class类的名字必须和.java文件名相同。
信息隐藏原则
类的成员属性,是私有的private
类的方法,是共有的public,通过方法修改成员属性的值
get和set方法是公有public的,统称为getter和setter
外界对类成员的操作只能通过get和set方法
可用Java IDE快速生成
Source-Generate Getters and Setters…
this
1 2 3 4 5 6 7 8 9 10 11 12 public class Test { private int id; public Test (int id) { this .id = id; } public int getId () { return id; } }
继承、接口和抽象类
继承
父类/基类/超类
Parent class/Base class/Super class
子类/派生类
Child class/Derived class
子类继承父类所有的属性和方法(但不能直接访问private成员)
根据信息隐藏原则:子类会继承父类所有的方法,可直接使用
子类也会继承父类的父类所有的属性和方法(但不能直接访问private成员)
子类可以通过调用父类的方法来访问父类的私有的成员属性
在同样方法名和参数情况下,本类的方法会比父类的方法优先级高
区别于C++,单根继承
单根继承原则:每个类都只能继承一个类
若不写extends,Java类默认继承java.lang.Object类
Java所有类从java.lang.Object开始,构造出一个类型继承树
Object类里面默认就有clone, equals, finalize, getClass, hashCode, toString等方法
每个子类的构造函数的第一句话,都默认调用父类的无参数构造函数super(),除非子类的构造函数第一句话是super,而且super语句必须放在第一条
如果构造函数的第一句话不是super,编译器会自动增加一句super();。如果构造函数第一句是自己写的super语句,编译器就不会自动添加。
抽象类
若方法只有方法名字,形参列表,没有方法体,那所在的类就被定义为抽象类。
若一个类暂时有方法未实现,需被定义为抽象类。
一个类继承于抽象类,就不能继承于其他的(抽象)类
子类可继承于抽象类,但一定要实现父类们所有abstract的方法,若不能完全实现,子类也必须被定义为抽象类
接口
如果类的所有方法都没有实现,这个类就算是接口interface
1 2 3 4 public interface Animal { public void eat () ; public void move () ; }
类只可以继承(extends)一个类,但可实现(implements)多个接口,继承和实现可以同时
接口不算类,或者说是特殊的类
接口可以继承(多个)接口,没有实现的方法将会叠加
类实现接口,就必须实现所有未实现的方法。若没有全部实现,只能成为一个抽象类
接口里可定义变量,但一般是常量 (经指正,成员变量只能是常量)
总结
抽象类和接口相同点:两者都不能被实例化,不能new操作
不同点:
抽象类abstract,接口interface
抽象类可有部分方法实现,接口所有方法不能有实现 (经指正,Java8后,接口可有默认实现)
一个类只能继承(extends)一个(抽象)类,实现(implenments)多个接口
接口可继承(extends)多个接口
抽象类有构造函数,接口没有构造函数
抽象类可有main,也能运行,接口没有main函数
抽象类方法可有private/protected,接口方法都是public
类转型
类型可相互转型,但只限制于有继承关系的类
子类可转换成父类(从大到小,向上转型),而父类不可转为子类(从小变大,向下转型)
父类转为子类有一种情况例外:这个父类本身就是从子类转化而来
1 2 Human obj1 = new Man ();Man obj2 = (Man) obj1;
多态
类型转换带来的作用就是多态
子类继承父类的所有方法,但子类可重新定义一个名字,参数和父类样的方法,这种行为是重写 (覆写,覆盖,overwrite/override,not overload(重载))
子类方法优先级高于父类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class Man extends Human { public void eat () { System.out.println("I can eat more" ); } public void plough () { } public static void main (String[] a) { Man obj1 = new Man (); obj1.eat(); Human obj2 = (Human) obj1; obj2.eat(); Man obj3 = (Man) obj2; obj3.eat(); } }
契约设计:类不会直接使用另外一个类,而是采用接口的形式,外部可以“空投”这个接口下的任意子类对象
static、final和常量设计
static
static关键字可作用在:
变量
static变量只依赖于类存在(通过类即可访问),不依赖于对象实例存在。
所有对象实例,关于同一个变量的值都共享存储在一个共同的空间(栈)。
方法
静态方法无需通过对象来引用,而通过类名可直接引用。
静态方法中只能使用静态变量,不能使用非静态变量。
静态方法禁止引用非静态方法。
类
匿名方法块
static块只在类第一次被加载时调用,在程序运行期间,代码只运行一次
执行顺序:static块 > 匿名块 > 构造函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class StaticBlock { static { System.out.println("static 块" ); } { System.out.println("匿名块" ); } public StaticBlock () { System.out.println("构造函数" ); } { System.out.println("匿名块" ); } }
单例模式
又名单态模式,Singleton
限定某一个类在整个程序运行过程中,只能保留一个实例对象在内存空间
GoF的23中设计模式(Design Pattern)(创建型、结构型、行为型)中经典的一种,属于创建型模式类型
采用static来共享对象实例
采用private构建函数,防止外界new操作
final
final关键字用来修饰
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class FinalObject { int a = 10 ; } public class FinalObjectTest { public static void main (String[] args) { final FinalObject obj1 = new FinalObject (); System.out.println(obj1.a); obj1.a = 20 ; System.out.println(obj1.a); obj1 = new FinalObject (); } }
常量设计
Java没有constant关键字
不能修改, final
不会修改/只读/只要一份, static
方便访问public
Java中的常量
public static final
建议变量名字全大写,以连字符相连,如UPPER_BOUND
一种特殊的常量:接口内定义的变量默认是常量
常量池
Java为很多基本类型的包装类/字符串都建立常量池
常量池:相同的值只存储一份,节省内存,共享访问
基本类型的包装类
Boolean: true, false
Byte:-128~127
Short, Integer, Long:-128~127
Character:0~127
不包括Float, Double:没有缓存(常量池)
1 2 3 4 5 6 7 8 9 10 11 Long l1 = -128L ;Long l2 = -128L ;System.out.println(String.valueOf(l1==l2)); Float f1 = 0.5f ;Float f2 = 0.5f ;System.out.println(String.valueOf(f1 == f2)); Double d1 = 0.5 ;Double d2 = 0.5 ;System.out.println(String.valueOf(d1 == d2));
Java为常量字符串都建立常量池缓存机制
基本类型的包装类和字符串有两种创建方式
常量式(字面量)复制创建,放在栈内存(将被常量化)
1 2 Integer a = 10 ;String b = "abc" ;
new对象进行创建,放在堆内存(不会常量化)
1 2 Integer c = new Integer (10 );String d = new String ("abc" );
这两种创建方式导致创建的对象存放的位置不同
栈内存读取速度快但容量小
堆内存读取速度慢但容量大
基本类型和包装类比较,将对包装类自动拆箱
对象比较,比较地址
加法+会自动拆箱
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 int i1 = 10 ;Integer i2 = 10 ;System.out.println(i1 == i2); Integer i3 = new Integer (10 );System.out.println(i1 == i3); System.out.println(i2 == i3); Integer i4 = new Integer (5 );Integer i5 = new Integer (5 );System.out.println(i1 == (i4+i5)); System.out.println(i2 == (i4+i5)); System.out.println(i3 == (i4+i5)); Integer i6 = i4 + i5;System.out.println(i1 == i6); System.out.println(i2 == i6); System.out.println(i3 == i6);
常量赋值(堆内存)和new创建(栈内存)不是同一个对象
编译器只会优化确定的字符串,并缓存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 String s0 = "abcdef" ;String s1 = "abc" ;String s2 = "abc" ;String s3 = new String ("abc" );String s4 = new String ("abc" );System.out.println(s1 == s2); System.out.println(s1 == s3); System.out.println(s3 == s4); String s5 = s1 + "def" ;String s6 = "abc" + "def" ;String s7 = "abc" + new String ("def" );System.out.println(s5 == s6); System.out.println(s6 == s7); System.out.println(s0 == s6); String s8 = s3 + "def" ;String s9 = s4 + "def" ;String s10 = s3 + new String ("def" );System.out.println(s8 == s9); System.out.println(s8 == s10); System.out.println(s9 == s10);
不可变对象和字符串
不可变对象
不可变对象(Immutable Object)
一旦创建,该对象(状态/值)不能被更改
其内在的成员变量的值不能修改
如八个基本型别的包装类
String, BigInteger和BigDecimal等
可变对象(Mutable Object)
普通对象
1 2 3 4 5 String a = new String ("abc" );String b = a;System.out.println(b); a = "def" ; System.out.println(b);
不可变对象是指值对象不再修改,即abc不会被修改,而指针(句柄/变量名)a的指向可以修改。
不可变对象,也是传指针(引用)
由于不可变,临时变量指向新内存,外部实参的指针不改动
1 2 3 4 5 6 7 public static void change (String b) { b = "def" ; } a = new String ("abc" ); change(a); System.out.println(a);
a->abc
b–^
change(a)后
a->abc
b->def
字符串
不可变对象
字符串内容比较:equals方法
是否指向同一个对象:指针比较==
1 2 String a = "abc" ;a = a + "def" ;
使用SrringBuffer/StringBuilder类的append方法进行修改
StringBuffer/StringBuilder的对象都是可变对象
StringBuffer(同步,线程安全,修改快速),StringBuilder(不同步,线程不安全,修改更快)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public static void changeValue (int a) { a = 10 ; } public static void changeValue (String s1) { s1 = "def" ; } public static void changeValue (StringBuffer s1) { s1.append("def" ); } public static void main (String[] args) { int a = 5 ; String b = "abc" ; StringBuffer c = new StringBuffer ("abc" ); changeValue(a); changeValue(b); changeValue(c); System.out.println(a); System.out.println(b); System.out.println(c); }
package, import和classpath
package和import
若所有Java类都是放置在同一个目录下的,类之间相互调用无需显式声明调用
同一个目录下,两个类的名字不能相同
文件过多不方便查找
Java支持多个目录放置Java,并通过package/import/classpath/jar等机制配合使用,可支持跨目录放置和调用Java类
1 2 3 4 package cn.edu.pku;public class PackageExample { }
类全称cn.edu.pku.PackageExample,短名称PackageExample
引用类时,必须采用全称引用;程序正文可用短名称
PackageExample.java必须严格放置在cn/edu/pku目录下
包名尽量唯一
域名是唯一的,因此常用域名做包名
域名逆序:cn.edu.pku,范围通常从大到小
类的完整名字:包名+类名,cn.edu.pku.PackageExample
包名:和目录层次一样,cn\edu\pku\PackageExample.java
包具体放在上面位置不重要,编译和运行时再制定
举个栗子
1 2 3 4 5 6 7 8 9 10 11 12 package cn.edu.pku;import cn.edu.pku.PackageExample;public class PackageExampleTest { public static void main (String[] args) { PackageExample obj = new PackageExample (); } }
“*”代表这个目录下所有文件,但不包括子文件夹和子文件夹内的文件。
import规则
import必须全部放在package之后,类定义之前
package必须是第一句话
多个import的顺序无关
可用来引入一个目录下的所有类,比如import java.lang. ;
注意:不能递归包含其下各个目录下的文件
import尽量精确,不推荐*,以免新增同名程序会使老程序报错
jar
用于可执行程序文件的传播,实际上是一组class文件的压缩包
项目引入一个jar文件,就可以使用jar文件中所有类(.class文件),无需类的源码(.java文件)。
jar只包含.class文件,没有.java文件,不会泄露源码。不过Java里的一些反编译工具,可从class反编译到Java。
利用Eclipse的Export功能导出jar文件
选中项目,点击顶部菜单File-Export-Java-Jar File
classpath
手动创建c:\temp\cn\com\test\Man.java
编译:
1 javac c:\temp\cn\com\test\Man.java
运行:
1 java -classpath .;c:\temp cn.com.test.Man
-classpath固定格式参数,简写为-cp
.;c:\temp
windows是分号,Linux/Mac是冒号
当在某一个子路径找到所需类后,后续子路径不再寻找
在classpath中,子路径排在前面优先级高
当所有子路径都寻找不到所需类,系统会报告NoClassDefFoundException错误
编译和运行规则
编译一个类,需java文件全路径,包括拓展名
运行一个类,需写类名全称(非文件路径),无需写扩展名
编译类时,需给出这个类所依赖的类(包括依赖的类再次依赖的所有其他类)的所在路径
运行类时,需给出这个类,以及被依赖类的路径总和
classpath参数也可以包含jar包。若路径内有空格,请将classpath参数整体加双引号。
1 java -classpath ".;c:\test.jar;c:\temp;c:\a bc" cn.com.test.Man
Java访问权限
private:私有,只能本类访问
default(通常忽略不写):同一个包内访问
protected: 同一个包,子类均可以访问
public:公开,所有类都可访问
四种都可用来修饰成员变量、成员方法、构造函数
default和public可修饰类
1 2 class A { }public class B { }
权限表
同一个类 同一个包 不同包的子类 不同包的非子类
private √
default √ √
protected √ √ √
public √ √ √ √
Java常用类
Java类库文档
https://docs.oracle.com/en/
https://docs.oracle.com/en/java/javase/13/docs/api/index.html
这些文档原先是程序中的注释。利用JavaDoc技术,将这些注释抽取出来,组织形成的以HTML为表现形式的API文档。
数字类
Java数字类
整数Short, Int, Long
浮点数Float, Double
大数类BigInteger(大整数), BigDecimal(大浮点数)
大数类没有限制,可表示无穷大的数字
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 BigInteger b1 = new BigInteger ("123456789" );BigInteger b2 = new BigInteger ("987654321" );System.out.println(b2.add(b1)); System.out.println(b2.subtract(b1)); System.out.println(b2.multiply(b1)); System.out.println(b2.divide(b1)); System.out.println(b2.max(b1)); System.out.println(b2.min(b1)); BigInteger result[] = b2.divideAndRemainder(b1); System.out.println("商是:" + result[0 ] + ";余数是:" + result[1 ]); System.out.println(b1.equals(b2)); int flag = b1.compareTo(b2);if (flag == -1 ) System.out.println("比较操作:b1<b2" ); else if (flag == 0 ) System.out.println("比较操作:b1==b2" ); else System.out.println("比较操作:b1>b2" );
尽量采用字符串对BigDecimal赋值,这样精度更准确。
1 2 3 4 5 6 7 8 System.out.println(new BigDecimal ("2.3" )); System.out.println(new BigDecimal (2.3 )); BigDecimal num1 = new BigDecimal ("10" );BigDecimal num2 = new BigDecimal ("3" );BigDecimal num3 = num1.divide(num2, 3 , BigDecimal.ROUND_HALF_UP);System.out.println(num3);
BigDecimal做除法操作,需要注意截断,防止出现无限循环小数。
随机数类Random
nextInt() 返回一个随机Int
nextInt(int a) 返回一个[0,a)之间的随机int
nextDouble() 返回一个[0.0,1.0]之间double
ints方法批量返回随机数数组
Math.random() 返回一个[0.0,1.0]之间double
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 Random rd = new Random ();System.out.println(rd.nextInt()); System.out.println(rd.nextInt(100 )); System.out.println(rd.nextLong()); System.out.println(rd.nextDouble()); System.out.println(Math.round(Math.random()*10 )); rd.ints(); int [] arr = rd.ints(10 ).toArray();for (int i = 0 ;i < arr.length; i++){ System.out.println(arr[i]); } arr = rd.ints(5 , 10 , 100 ).toArray(); for (int i = 0 ; i < arr.length; i++){ System.out.println(arr[i]); } arr = rd.ints(10 ).limit(5 ).toArray(); for (int i = 0 ;i < arr.length; i++){ System.out.println(arr[i]); }
工具类Math
java.math包
java.lang.Math
绝对值函数abs
对数函数log
比较函数max、min
幂函数pow
四舍五入函数round
向下取整floor
向上取整ceil
字符串相关类
String
Java使用频率最高的类
一个不可变对象,加减操作性能较差
常用:
charAt, concat, contains, endsWith, equals, equalsIgnoreCase, hashCode, indexOf, length, matches, replace, replaceAll, split, startsWith, subString, trim, valueOf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 String a = "123;456,789,123 " ;System.out.println(a.charAt(0 )); System.out.println(a.indexOf(";" )); System.out.println(a.concat(";000" )); System.out.println(a.contains("000" )); System.out.println(a.endWith("000" )); System.out.println(a.equals("000" )); System.out.println(a.equalsIgnoreCase("000" )); System.out.println(a.length()); System.out.println(a.trim()); String[] b = a.split(";" ); for (int i = 0 ; i < b.length; i++){ System.out.println(b[i]); } System.out.println(a.substring(2 , 5 )); System.out.println(a.replace("1" , "a" )); System.out.println(a.replaceAll("1" , "a" ); String s1 = "123456789" ;String s2 = s1.replace("?" , "a" );String s3 = s1.replaceAll("[?]" , "a" );
trim方法去掉的是字符串的前后空格,无法去除字符串中间的空格。
可变字符串
StringBuffer(字符串加减,同步,性能好)
StringBuilder(字符串加减,不同步,性能更好)
StringBuffer/StringBuilder:方法一样,区别在同步
append/insert/delete/replace/substring
length字符串实际大小,capacity字符串占用空间大小
trimToSize():去除空隙,将字符串存储压缩到实际大小
如有大量append,事先预估大小,再调用相应构造函数
时间类
java.util.Date(基本废弃,Deprecated)
getTime(),返回自1970.1.1以来的毫秒数
java.spl.Date(和数据库对应的时间类)
Calendar是目前程序中最常用的,但是是抽象类
Calendar gc = Calendar.getInstance();
Calendar gc = new GregorianCalendar();
简单工厂模式
查看CalendarClassTest.java
Calendar
get(Field) 来获取时间中每个属性的值,注意,月份0-11
getTime() 返回相应的Date对象
getTimeInMillis() 返回自1970.1.1以来的毫秒数
set(Field) 设置时间字段
add(field, amount) 根据指定字段增加/减少时间
roll(field, amount) 根据指定字段增加/减少时间,但不影响上一级的时间段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 Calendar calendar = Calendar.getInstance();public void test1 () { int year = calendar.get(Calendar.YEAR); int month = calendar.get(Calendar.MONTH) + 1 ; int day = calendar.get(Calendar.DAY_OF_MONTH); int hour = calendar.get(Calendar.HOUR); int minute = calendar.get(Calendar.MINUTE); int second = calendar.get(Calendar.SECOND); int weekday = calendar.get(Calendar.DAY_OF_WEEK); } public void test2 () { calendar.add(Calendar.YEAR, 1 ); int year = calendar.get(Calendar.YEAR); int month = calendar.get(Calendar.MONTH) + 1 ; int day = calendar.get(Calendar.DAY_OF_MONTH); } public void test3 () { int currentMonth = 6 ; calendar.set(calendar.get(Calendar.YEAR), currentMonth, 1 ); calendar.add(Calenadr.DATA, -1 ); int day = calendar.get(Calendar.DAY_OF_MONTH); } public void test4 () { calendar.set(Calendar.YEAR, 2000 ); System.out.println("现在是" + calendar.get(Calendar.YEAR) + "年" ); calendar.set(2018 , 1 , 2 ); int year = calendar.get(Calendar.YEAR); int month = calendar.get(Calendar.MONTH) + 1 ; int day = calendar.get(Calendar.DAY_OF_MONTH); } public void test5 () { calendar.set(2018 , 7 , 8 ); calendar.add(Calendar.DAY_OF_MONTH, -8 ); int year = calendar.get(Calendar.YEAR); int month = calendar.get(Calendar.MONTH) + 1 ; int day = calendar.get(Calendar.DAY_OF_MONTH); System.out.println("2018.7.8,用add减少8天,现在是" + year + "." + month + "." + day); calendar.set(2018 , 7 , 8 ); calendar.roll(Calendar.DAY_OF_MONTH, -8 ); int year = calendar.get(Calendar.YEAR); int month = calendar.get(Calendar.MONTH) + 1 ; int day = calendar.get(Calendar.DAY_OF_MONTH); System.out.println("2018.7.8,用roll减少8天,现在是" + year + "." + month + "." + day); }
roll函数做加减法,只影响当前字段,不影响进位。add函数则会影响进位。
Java 8 退出新的时间API
java.time包
旧的设计不好(重名的类、线程不安全等)
新版本优点:不变性,在多线程环境下;遵循设计模式,设计得更好,可扩展性强
Java 8 时间包概述
java.time包:新的Java日期/时间API的基础包
主要类:LocalDate日期类
LocalTime时间类(时分秒-纳秒)
LocalDateTime:LocalDate + LocalTime
Instant时间戳
java.time.chrono包:为非ISO的日历系统定义了一些泛化的API
java.time.format包:格式化和解析日期时间对象的类
java.time.temporal包:包含一些时态对象,可以用其找出关于日期/时间对象的某个特定日期或时间
java.time.zone包:包含支持不同时区以及相关规则的类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 LocalDate today = LocalDate.now();LocalDate firstDay_2014 = LocalDate.of(2014 , Month.JANUARY, 1 );LocalDate todayBeijing = LocalDate.now(ZoneId.of("Asia/Shanghai" ));LocalDate dateFromBase = LocalDate.ofEpochDay(365 );LocalDate hundredDay2014 = LocalDate.ofYearDay(2014 , 100 );LocalTime time = LocalTime.now();LocalTime specificTime = LocalTime.of(12 , 20 , 25 , 40 );LocalTime timeSH = LocalTime.mow(ZoneId.of("Asia/Shanghai" ));LocalTime specificSecondTime = LocalTime.ofSecondOfDay(10000 );Instant timestamp = Instant.now();Instant specificTime = Instant.ofEpochMilli(timestamp.toEpochMilli());Date date = Date.from(timestamp);LocalDate today = LocalDate.now();System.out.println("Year " + today.getYear() + "is Leap Year" + today.isLeapYear()); System.out.println("Today is before 01/01/2015" + today.isBefore(LocalDate.of(2015 ,1 ,1 ))); System.out.println("Current Time =" + today.atTime(LocalTime.now())); System.out.println("10 days after today will be " + today.plusDays(10 )); System.out.println("3 weeks after today will be " + today.plusWeeks(3 )); System.out.println("20 months after today will be " + today.plusMonths(20 )); System.out.println("10 days after today will be " + today.minusDays(10 )); System.out.println("3 weeks after today will be " + today.minusWeeks(3 )); System.out.println("3 weeks after today will be " + today.minusMonths(20 )); System.out.println("First date of this month = " + today.with(TemporalAdjusters.firstDayOfMonth())); LocalDate lastDayOfYear = today.with(TemporalAdjusters.lastDayOfYear());System.out.println("Last date of this year = " + lastDayOfYear); Period period = today.until(lastDayOfYear);System.out.println("Period Format = " + period); System.out.println("Months remaining in the year = " + period.getMonths());
java.text包java.text.Format的子类
NumberFormat:数字格式化,抽象类
– DecimalFormat 工厂模式
例如:将1234567格式化输出为1,234,567
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 DecimalFormat df1,df2; df1 = new DecimalFormat ("#.00" ); df2 = new DecimalFormat ("0.00" ); System.out.println(df1.format(0.1 )); System.out.println(df2.format(0.1 )); df1 = new DecimalFormat ("0.00" ); df2 = new DecimalFormat ("0.##" ); System.out.println(df1.format(0.1 )); System.out.println(df2.format(0.1 )); df1 = new DecimalFormat ("0.00" ); df2 = new DecimalFormat ("#.00" ); System.out.println(df1.format(2 )); System.out.println(df2.format(2 )); System.out.println(df1.format(20 )); System.out.println(df2.format(20 )); System.out.println(df1.format(200 )); System.out.println(df2.format(200 ));
MessageFormat:字符串格式化
支持多个参数-值对位复制文本S
支持变量的自定义格式
例如将“Hello {1}”根据变量值格式化为Hello World
1 2 3 4 5 6 7 String message = "{0}{1}{2}{3}{4}{5}{6}{7}{8}{9}{10}{11}{12}{13}{14}{15}{16}" ;object[] array = new Object []{"A" ,"B" ,"C" ,"D" ,"E" ,"F" ,"G" ,"H" ,"I" ,"J" ,"K" ,"L" ,"M" ,"N" ,"O" ,"P" ,"Q" }; String value = MessageFormat.format(message, array);System.out.println(value); message = "oh, {0,number,#.##} is a good number" ; array = new Object []{new Double (3.1415 )}; value = MessageFormat.format(message, array);
输出
1 2 ABCDEFGHIJKLMNOPQ oh, 3.14 is a good number
DateFormat:日期/时间格式化,抽象类
– SimpleDateFormat 工厂模式
parse:将字符串格式化为时间对象
format:将时间对象格式化为字符串
如将当前时间转为YYYY-MM-DD HH24:MI:SS输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 String strDate = "2008-10-19 10:11:30.345" ;String pat1 = "yyyy-MM-dd HH:mm:ss.SSS" ;String pat2 = "yyyy年MM月dd日 HH时mm分ss秒SSS毫秒" ;SimpleDateFormat sdf1 = new SimpleDateFormat (pat1); SimpleDateFormat sdf2 = new SimpleDateFormat (pat2);Date d = null ;try { d = sdf1.parse(strDate); }catch (Exception e){ e.printStackTrace(); } System.out.println(sdf2.format(d));
java.time.format包下
java.time.format.DateFormatter:时间格式化
JDK 8 发布,线程安全(vs SimpleDateFormat 线程不安全)
ofPattern:设定时间格式
parse:将字符串格式化为时间对象
format:将时间对象格式化为字符串
如将当前时间转为YYYY-MM-DD HH24:MI:SS输出
1 2 3 4 5 6 7 8 9 10 11 String dateStr = "2020年1月1日" ;DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日" );LocalDate date = LocalDate.parse(dateStr, formatter);System.out.println(date.getYear() + "-" + date.getMonthValue() + "-" + date.getDayOfMonth()); LocalDateTime now = LocalDateTime.now();DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy年MM月dd日 hh:mm:ss" );String nowStr = now.format(format);System.out.println(nowStr);
LocalDate类的月份是1-12,不用加1;Calendar类的月份是0-11,需要加1。
异常和异常处理
异常
Java异常分成Exception(程序相关)和Error(系统相关)。
Exception分为RuntimeException程序自身的错误(如5/0,空指针,数组越界)和非RuntimeException外界相关的错误(如打开一个不存在的文件,加载一个不存在的类)。
IOException是非RuntimeException的典型示例。
另一种分类方法:
Unchecked Exception:(编译器不会辅助检查的,需要管理员自己管的)异常,包括Error子类和RuntimeException子类。
非RuntimeException的Exception的子类:(编译器会辅助检查的)异常,checked exception。
注意:编译器会检查程序是否为checked exception配置了处理。若没有处理,会报错。
异常处理
try-catch-finally:一种保护代码正常运行的机制。
异常结构
try…catch(catch可以有多个,下同)
try…catch…finally
try…finally
try必须有,catch和finally至少要有一个
try:正常业务逻辑代码
catch:当try发生异常,将执行catch代码。若无异常,绕之。
finally:当try或catch执行结束后,必须要执行finally。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 try { int a = 5 /2 ; System.out.println(a); } catch (Exception ex){ ex.printStackTrace(); } finally { System.out.println("1 over" ); } try { int a = 5 /0 ; System.out.println(a); } catch (Exception ex){ ex.printStackTrace(); } finally { System.out.println("2 over" ); } try { int a = 5 /0 ; System.out.println(a); } catch (Exception ex){ ex.printStackTrace(); int a = 5 /0 ; } finally { System.out.println("3 over" ); }
3中,第一个异常执行完finally后再执行第二个异常。(catch内部再次发生异常也不影响finally的正常运行。)
进入catch块后,并不会返回到try发生异常的位置,也不会执行后续的catch块,一个异常只能进入一个catch块。
一般将小异常(具体的异常子类,如ArithmeticException)写在前面,大(宽泛)的异常(如Exception)写在末尾。
允许包含:
1 2 3 4 5 6 7 8 9 try{ try-catch-finally结构 } catch(){ try-catch-finally结构 } finally{ try-catch-finally结构 }
方法存在可能异常的语句,但不处理,可使用throws来声明异常。
调用带有throws异常(checked exception)的方法,要么处理这些异常,或者再次向外throws,直到main函数为止。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class ThrowsDemo { public static void main (String [] args) { try { int result = new Test ().divide(3 ,1 ); System.out.println("1 result:" + result); } catch (ArithmeticException ex) { ex.printStackTrace(); } int result = new Test ().divide(3 ,0 ); System.out.println("2 result:" + result); } } class Test { public int divide (int x, int y) throws ArithmeticException { int result = x/y; return x/y; } }
一个方法被覆盖,覆盖它的方法不限抛出相同的异常,或者异常的子类。
如果父类的方法抛出多个异常,那么重写的子类方法必须抛出那些异常的子集,也就是不能抛出新的异常。
Father.java
1 2 3 4 5 6 public class Father { public void f1 () throws ArithmeticException { } }
Son.java
1 2 3 public class Son extends Father { public void f1 () throws Exception }
自定义异常
自定义异常需要继承Exception类或其子类。
继承自Exception,就变成Checked Exception。
继承自RuntimeException,就变成Unchecked Exception。
(非RuntimeException,是Checked Exception,IDE辅助检查;RuntimeException,是Unchecked Exception,IDE不会辅助检查,是程序员自行处理。)
自定义重点在构造函数
调用父类Exception的message构造函数
可自定义自己的成员变量
在程序中采用throw主动抛出异常
MyException.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public class MyException extends Exception { private String returnCode; private String returnMsg; public MyException () { super (); } public MyException (String returnMsg) { super (returnMsg); this .returnMsg = returnMsg; } public MyException (String returnCode, String returnMsg) { super (returnMsg); this .returnCode = returnCode; this .returnMsg = returnMsg; } public String getReturnCode () { return returnCode; } public String getReturnMsg () { return returnMsg; } }
在方法内部程序中,抛出异常采用throw关键字;在方法头部声明中,声明异常采用throws关键字。
MyExceptionTest.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class MyExceptionTest { public static void testException () throws MyException { throw new MyException ("10001" , "The reason of myException" ); } public static void main (String[] args) { try { MyExceptionTest.testException(); } catch (MyException e) { e.printStackTrace(); System.out.println("returnCode:" + e.getReturnCode()); System.out.println("returnCode:" + e.getReturnMsg()); } } }
注释1:main函数里调用了一个声明MyException异常的方法,但没处理。因为MyException继承Exception,是属于非RuntimeException,也就是Checked Exception,因此编译器会检查到程序没处理而报错。解法:1)采用try-catch-finally结构 2)main函数也throws MyException。
DivideByMinusException.java
1 2 3 4 5 6 7 8 9 10 11 12 public class DivideByMinusException extends Exception { int divisor; public DivideByMinusException (String msg, int divisor) { super (msg); this .divisor = divisor; } public int getDevisor () { return this .getDevisor(); } }
Student.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 public class Student { public int divide (int x, int y) { return x/y; } public static void main (String[] args) throws DivideByMinusException { Student newton = new Student (); newton.divide5(5 ,-2 ); } public int divide2 (int x,int y) { int result; try { result = x/y; System.out.println("result is " + result); } catch (ArithmeticException ex) { System.out.println(ex.getMessage()); return 0 ; } catch (Exception ex) { ex.printStackTrace(); return 0 ; } } public int divide3 (int x,int y) throws ArithmeticException { return x/y; } public int divide4 (int x, int y) { return divide3(x,y); } public int divide5 (int x,int y) throws DivideByMinusException { try { if (y < 0 ) { throw new DivideByMinusException ("The divisor is negative" , y); } return divide3(x,y); } catch (ArithmeticException ex) { ex.printStackTrace(); return 0 ; } } }
对于CheckedException,编译器要求调用者处理,要么采用try-catch-finally处理,要么采用throws声明异常。
数据结构
数组
1 2 3 4 5 6 7 8 9 10 11 12 13 int a[];int [] b;int [] c = new int [2 ];c[0 ] = 1 , c[1 ] = 2 ; int d[] = new int []{1 ,2 ,3 };int d1[] = {1 ,2 ,3 };
数组索引
数组的length属性标识数组的长度
从0开始,到length-1
数组不能越界访问,否则会报ArrayIndexOutOfBoundsException异常
1 2 3 4 5 6 7 8 9 10 11 for (int i = 0 ; i < d.length; i++){ System.out.println(d[i]); } for (int e : d){ System.out.println(e); }
1 2 3 4 5 6 7 8 int a[][] = new int [2 ][3 ];int b[][];b = new int [3 ][]; b[0 ] = new int [3 ]; b[1 ] = new int [4 ]; b[2 ] = new int [5 ];
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int k = 0 ;for (int i = 0 ; i < a.length; i++){ for (int j = 0 ; j < a[i].length; j++) { a[i][j] = ++k; } } for (int [] items : a){ for (int item : items) { System.out.println(item + ", " ); } System.out.println(); }
JCF
Java Collection Framework
容器:能够存放数据的空间结构
数组/多维数组,只能线性存放
列表/散列集/树/…
容器框架:为表示和操作容器而规定的一种标准体系结构
对外的接口:容器中所能存放的抽象数据类型
接口的实现:可复用的数据结构
算法:对数据的查找和排序
容器框架优点:提高数据存取效率,避免程序员重复劳动
C++的STL, Java的JCF
Java 1.1和以前的数据结构
Vector, Stack, Hashtable, Enumeration等
Java 1.2和以后,JCF集合框架
功能更强大,易于学习
接口和实现分离,多种设计模式更灵活
泛型设计
主要由列表(List)、散列集合(Set)、映射(Map)组成。(还有Arrays和Collection工具类)
Dictionary和Hashtable属于JDK1.1的类,已经较少使用了。
早期接口Enumeration
JCF的集合接口是Collection
add, contains, remove, size
iterator
JCF的迭代器接口Iterator
hasNext, next, remove
JCF主要的数据结构实现类
列表(List, ArrayList, LinkedList)
集合(Set, HashSet, TreeSet, LinkedHashSet)
映射(Map, HashMap, TreeMap, LinkedHashMap)
JCF主要的算法类
Arrays: 对数组进行查找和排序等操作
Collections: 对Collection及其子类进行排序和查找操作
List
List 列表
有序的Collection
允许重复元素
{1, 2, 3, {1, 2}, 1, 3}
List主要实现
ArrayList(非同步)
LinkedList(非同步)
Vector(同步)
ArrayList
以数组实现的列表,不支持同步
利用索引位置可以快速定位访问
不适合指定位置的插入、删除操作
适合变动不大,主要用于查询的数据
和Java数组相比,其容量是可动态调整的//Java数组是静态的,长度确定不可修改
ArrayList在元素填满容器时会自动扩充容器大小的50%
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 import java.util.ArrayList;public class ArrayListTest { public static void main (String[] a) { ArrayList<Integer> al = new ArrayList <Integer>(); al.add(1 ); al.add(2 ); al.add(3 ); al.add(4 ); al.add(new Integer (6 )); System.out.println(al.get(3 )); al.remove(3 ); al.add(3 , 0 ); ArrayList<Integer> as = new ArrayList <Integer>(100000 ); for (int i = 0 ; i < 100000 ; i++) { as.add(i); } traverseByIterator(as); traverseByIndex(as); traverseByFor(as); } public static void traverseByIterator (ArrayList<Integer> al) { long startTime = System.namoTime(); Iterator<Integer> iter1 = al.iterator(); while (iter1.hasNext()){ iter1.next(); } long endTime = System.nanoTime(); long duration = endTime - startTime; System.out.println(duration + "纳秒" ); } public static void traverseByIndex (ArrayList<Integer> al) { long startTime = System.namoTime(); for (int i = 0 ; i< al.size(); i++) { al.get(i); } long endTime = System.nanoTime(); long duration = endTime - startTime; System.out.println(duration + "纳秒" ); } public static void traverseByFor (ArrayList<Integer> al) { long startTime = System.namoTime(); for (Integer item : al) { ; } long endTime = System.namoTime(); long duration = endTime - startTime; System.out.println(duration + "纳秒" ); } }
结果表明:for循环遍历最快,随机索引值遍历和for差距不大,迭代器最慢。
LinkedList
以双向链表实现的列表,不支持同步
可被当作堆栈、队列和双端队列进行操作
顺序访问高效、随机访问较差,中间插入和删除高效
适用于经常变化的数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 import java.util.ArrayList;public class LinkedListTest { public static void main (String[] a) { LinkedList<Integer> ll = new LinkedList <Integer>(); ll.add(1 ); ll.add(2 ); ll.add(3 ); ll.add(4 ); System.out.println(ll.size()); ll.addFirst(0 ); ll.add(3 ,5 ); ll.remove(3 ); LinkedList<Integer> list = new LinkedList <Integer>(); for (int i = 0 ; i < 100000 ; i++) { list.add(i); } traverseByIterator(list); traverseByIndex(list); traverseByFor(list); } public static void traverseByIterator (LinkedList<Integer> list) { long startTime = System.namoTime(); Iterator<Integer> iter1 = list.iterator(); while (iter1.hasNext()){ iter1.next(); } long endTime = System.nanoTime(); long duration = endTime - startTime; System.out.println(duration + "纳秒" ); } public static void traverseByIndex (LinkedList<Integer> list) { long startTime = System.namoTime(); for (int i = 0 ; i< list.size(); i++) { list.get(i); } long endTime = System.nanoTime(); long duration = endTime - startTime; System.out.println(duration + "纳秒" ); } public static void traverseByFor (LinkedList<Integer> list) { long startTime = System.namoTime(); for (Integer item : list) { ; } long endTime = System.namoTime(); long duration = endTime - startTime; System.out.println(duration + "纳秒" ); } }
结果:for循环最快,迭代器稍微慢一些,随机索引特别慢。
性能比较
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 import java.util.ArrayList;public class ListCompareTest { public static void main (String[] args) { int times = 10 * 1000 ; ArrayList<Integer> arrayList = new ArrayList <Integer>(); LinkedList<Integer> linkedList = new LinkedList <Integer>(); long startTime = System.nanoTime(); for (int i = 0 ; i < times; i++) { arrayList.add(0 , i); } long endTime = System.namoTime(); long duration = endTime - startTime; System.out.println(duration + "纳秒" ); startTime = System.nanoTime(); for (int i = 0 ; i < times; i++) { linkedList.add(0 , i); } endTime = System.namoTime(); duration = endTime - startTime; System.out.println(duration + "纳秒" ); startTime = System.nanoTime(); for (int i = 0 ; i < times; i++) { arrayList.get(i); } endTime = System.namoTime(); duration = endTime - startTime; System.out.println(duration + "纳秒" ); startTime = System.nanoTime(); for (int i = 0 ; i < times; i++) { linkedList.get(i); } endTime = System.namoTime(); duration = endTime - startTime; System.out.println(duration + "纳秒" ); startTime = System.nanoTime(); for (int i = 0 ; i < times; i++) { arrayList.remove(0 ); } endTime = System.namoTime(); duration = endTime - startTime; System.out.println(duration + "纳秒" ); startTime = System.nanoTime(); for (int i = 0 ; i < times; i++) { linkedList.remove(0 ); } endTime = System.namoTime(); duration = endTime - startTime; System.out.println(duration + "纳秒" ); } }
结果:add ArrayList时间远大于LinkedList
get ArrayList时间远小于LinkedList
remove ArrayList时间远大于LinkedList
结论:ArrayList适用于较多查询的(静态)数据,LinkedList适用于频繁增删的数据。
Vector(同步)
和ArrayList类似,可变数组实现的列表
Vector同步,适合在多线程下使用
原先不属于JCF框架,属于Java最早的数据结构,性能较差
从JDK 1.2开始,Vector被重写,并纳入到JCF
官方文档建议在非同步情况下,优先采用ArrayList
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 import java.util.ArrayList;public class VectorTest { public static void main (String[] args) { Vector<Integer> v = new Vector <Integer>(); v.add(1 ); v.add(2 ); v.add(3 ); v.remove(2 ); v.add(1 , 5 ); System.out.println(v.size()); Vector<Integer> v2 = new Vector <Integer>(100000 ); for (int i = 0 ; i < 100000 ; i++) { v2.add(i); } traverseByIterator(v2); traverseByIndex(v2); traverseByFor(v2); traverseByEnumeration(v2); } public static void traverseByIterator (Vector<Integer> v) { long startTime = System.namoTime(); Iterator<Integer> iter1 = v.iterator(); while (iter1.hasNext()){ iter1.next(); } long endTime = System.nanoTime(); long duration = endTime - startTime; System.out.println(duration + "纳秒" ); } public static void traverseByIndex (Vector<Integer> v) { long startTime = System.namoTime(); for (int i = 0 ; i< v.size(); i++) { v.get(i); } long endTime = System.nanoTime(); long duration = endTime - startTime; System.out.println(duration + "纳秒" ); } public static void traverseByFor (Vector<Integer> v) { long startTime = System.namoTime(); for (Integer item : v) { ; } long endTime = System.namoTime(); long duration = endTime - startTime; System.out.println(duration + "纳秒" ); } public static void traverseByEnumeration (Vector<Integer> v) { long startTime = System.namoTime(); for (Enumeration<Integer> enu = v.elements(); enu.hasMoreElements();) { enu.nextElement(); } long endTime = System.namoTime(); long duration = endTime - startTime; System.out.println(duration + "纳秒" ); } }
结果:for循环是最快的,位置索引第二名,Enumeration第三,迭代器最慢。
Set
确定性:对任意对象都能判定其是否属于某一个集合
互异性:集合内每个元素都是不相同的,内容互异
无序性:集合内的顺序无关
集合接口Set
HashSet 基于散列函数的集合,无序,不支持同步
基于HashMap实现的,可以容纳null元素,不支持同步
但以下方法可以实现同步
1 Set s = Collections.synchronizedSet(new HashSet(...));
add 添加一个元素
clear 清除整个HashSet
contains 判定是否包含一个元素
remove 删除一个元素 size 大小
retainAll 计算两个集合交集
TreeSet 基于树结构的集合,可排序的,不支持同步
LinkedHashSet 基于散列函数和双向链表的集合,可排序的,不支持同步
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 import java.util.ArrayList;import java.util.HashSet;import java.util.Iterator;public class HashSetTest { public static void main (String[] args) { HashSet<Integer> hs = new HashSet <Integer>(); hs.add(null ); hs.add(1 ); hs.add(2 ); hs.add(3 ); hs.add(40000 ); hs.add(5000000 ); hs.add(3 ); hs.add(null ); System.out.println(hs.size()); if (!hs.contains(6 )) { hs.add(6 ); } System.out.println(hs.size()); hs.remove(4 ); System.out.println(hs.size()); for (Integer item : hs) { System.out.println(item); } HashSet<String> set1 = new HashSet <String>(); HashSet<String> set2 = new HashSet <String>(); set1.add("a" ); set1.add("b" ); set1.add("c" ); set2.add("c" ); set2.add("d" ); set2.add("e" ); set1.retainAll(set2); System.out.println("交集是 " +set1); HashSet<Integer> hs2 = new HashSet <Integer>(); for (int i=0 ;i<100000 ;i++) { hs2.add(i); } traverseByIterator(hs2); traverseByFor(hs2); } public static void traverseByIterator (HashSet<Integer> hs) { long startTime = System.nanoTime(); Iterator<Integer> iter1 = hs.iterator(); while (iter1.hasNext()){ iter1.next(); } long endTime = System.nanoTime(); long duration = endTime - startTime; System.out.println(duration + "纳秒" ); } public static void traverseByFor (HashSet<Integer> hs) { long startTime = System.nanoTime(); for (Integer item : hs) { ; } long endTime = System.nanoTime(); long duration = endTime - startTime; System.out.println(duration + "纳秒" ); } }
迭代器慢于for循环。
LinkedHashSet
继承HashSet,也是基于HashMap实现的,可容纳null元素
不支持同步
1 Set s = Collections.synchronizedSet(new LinkedHashSet(...));
方法和HashSet基本一致
add,clear,contains,remove,size
通过一个双向链表维护插入顺序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 import java.util.ArrayList;import java.util.HashSet;import java.util.Iterator;import java.util.LinkedHashSet;public class LinkedHashSetTest { public static void main (String[] args) { LinkedHashSet<Integer> lhs = new LinkedHashSet <Integer>(); lhs.add(null ); lhs.add(1000 ); lhs.add(20 ); lhs.add(3 ); lhs.add(40000 ); lhs.add(5000000 ); lhs.add(3 ); lhs.add(null ); System.out.println(lhs.size()); if (!lhs.contains(6 )) { lhs.add(6 ); } System.out.println(lhs.size()); lhs.remove(4 ); System.out.println(lhs.size()); for (Integer item : lhs) { System.out.println(item); } LinkedHashSet<Integer> lhs2 = new LinkedHashSet <Integer>(); for (int i=0 ;i<100000 ;i++) { lhs2.add(i); } traverseByIterator(lhs2); traverseByFor(lhs2); } public static void traverseByIterator (LinkedHashSet<Integer> hs) { long startTime = System.nanoTime(); Iterator<Integer> iter1 = hs.iterator(); while (iter1.hasNext()){ iter1.next(); } long endTime = System.nanoTime(); long duration = endTime - startTime; System.out.println(duration + "纳秒" ); } public static void traverseByFor (LinkedHashSet<Integer> hs) { long startTime = System.nanoTime(); for (Integer item : hs) { ; } long endTime = System.nanoTime(); long duration = endTime - startTime; System.out.println(duration + "纳秒" ); } }
LinkedHashSet是保留顺序的,其遍历顺序和插入顺序一致;而HashSet没有保留顺序,其遍历顺序无序。
TreeSet
基于TreeMap实现的,不可以容纳null元素,不支持同步
1 SortedSet s = Collections.synchronizedSortedSet(new TreeSet (...));
add 添加一个元素
clear 清除整个TreeSet
contains 判定是否包含一个元素
remove 删除一个元素
size 大小
根据compareTo方法或指定Comparator排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 import java.util.ArrayList;import java.util.HashSet;import java.util.Iterator;import java.util.LinkedHashSet;import java.util.TreeSet;public class TreeSetTest { public static void main (String[] args) { TreeSet<Integer> ts = new TreeSet <Integer>(); ts.add(1000 ); ts.add(20 ); ts.add(3 ); ts.add(40000 ); ts.add(5000000 ); ts.add(3 ); System.out.println(ts.size()); if (!ts.contains(6 )) { ts.add(6 ); } System.out.println(ts.size()); ts.remove(4 ); System.out.println(ts.size()); for (Integer item : ts) { System.out.println(item); } TreeSet<Integer> ts2 = new TreeSet <Integer>(); for (int i=0 ;i<100000 ;i++) { ts2.add(i); } traverseByIterator(ts2); traverseByFor(ts2); } public static void traverseByIterator (TreeSet<Integer> hs) { long startTime = System.nanoTime(); Iterator<Integer> iter1 = hs.iterator(); while (iter1.hasNext()){ iter1.next(); } long endTime = System.nanoTime(); long duration = endTime - startTime; System.out.println(duration + "纳秒" ); } public static void traverseByFor (TreeSet<Integer> hs) { long startTime = System.nanoTime(); for (Integer item : hs) { ; } long endTime = System.nanoTime(); long duration = endTime - startTime; System.out.println(duration + "纳秒" ); } }
HashSet是无序输出的
LinkedHashSet是按照插入的顺序进行遍历输出的
TreeSet是按照所存储对象大小升序输出的
HashSet, LinkedHashSet, TreeSet的元素都只能是对象
HashSet和LinkedHashSet判定元素重复的原则
判定两个元素的hashCode返回值是否相同,不同则返回false
若两者hashCode相同,判定equals方法,不同则返回false
hashCode和equals方法是所有类都有的,因为Object类有
TreeSet判定元素重复的原则
需要元素继承自Comparable接口
比较两个元素的compareTo方法
HashSetJudgeRuleTest.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import java.util.HashSet;public class HashSetJudgeRuleTest { public static void main (String[] args) { HashSet<Cat> hs = new HashSet <Cat>(); hs.add(new Cat (1 )); hs.add(new Cat (2 )); hs.add(new Cat (3 )); hs.add(new Cat (3 )); System.out.println(hs.size()); } }
Cat.java
1 2 3 4 5 6 7 8 9 10 class Cat { private int size; public Cat (int size) { this .size = size; } }
Dog.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 class Dog { private int size; public Dog (int s) { size = s; } public int getSize () { return size; } public boolean equals (Object obj2) { System.out.println("Dog equals()~~~~~~~~~~~" ); if (0 ==size - ((Dog) obj2).getSize()) { return true ; } else { return false ; } } public int hashCode () { System.out.println("Dog hashCode()~~~~~~~~~~~" ); return size; } public String toString () { System.out.print("Dog toString()~~~~~~~~~~~" ); return size + "" ; } }
Tiger.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class Tiger implements Comparable { private int size; public Tiger (int s) { size = s; } public int getSize () { return size; } public int compareTo (Object o) { System.out.println("Tiger compareTo()~~~~~~~~~~~" ); return size - ((Tiger) o).getSize(); } }
Tiger实现Comparable接口,所以必须实现compareTo方法来比较大小。
compareTo方法具体规则如下:
1 int a = obj1.compareTo(obj2);
若a>0,则obj1>obj2;
若a0,则obj1 obj2;
若a<0,则obj1<obj2。
HashSet的元素判定规则只和hashCode、equals这2个方法有关,和compareTo方法无关。
Java的四个重要接口:
Comparable可比较的
Clonable可克隆的
Runnable可线程化的
Serializable可序列化的
ObjectHashSetTest.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 import java.util.HashSet;import java.util.Iterator;import java.util.LinkedHashSet;import java.util.TreeSet;public class ObjectHashSetTest { public static void main (String[] args) { System.out.println("==========Cat HashSet ==============" ); HashSet<Cat> hs = new HashSet <Cat>(); hs.add(new Cat (2 )); hs.add(new Cat (1 )); hs.add(new Cat (3 )); hs.add(new Cat (5 )); hs.add(new Cat (4 )); hs.add(new Cat (4 )); System.out.println(hs.size()); System.out.println("========================" ); LinkedHashSet<Cat> lhs= new LinkedHashSet <Cat>(); lhs.add(new Cat (2 )); lhs.add(new Cat (1 )); lhs.add(new Cat (3 )); lhs.add(new Cat (5 )); lhs.add(new Cat (4 )); lhs.add(new Cat (4 )); System.out.println(lhs.size()); System.out.println("==========Dog HashSet ==============" ); HashSet<Dog> hs2 = new HashSet <Dog>(); hs2.add(new Dog (2 )); hs2.add(new Dog (1 )); hs2.add(new Dog (3 )); hs2.add(new Dog (5 )); hs2.add(new Dog (4 )); hs2.add(new Dog (4 )); System.out.println(hs2.size()); System.out.println("========================" ); LinkedHashSet<Dog> lhs2= new LinkedHashSet <Dog>(); lhs2.add(new Dog (2 )); lhs2.add(new Dog (1 )); lhs2.add(new Dog (3 )); lhs2.add(new Dog (5 )); lhs2.add(new Dog (4 )); lhs2.add(new Dog (4 )); System.out.println(lhs2.size()); System.out.println("==========Tiger HashSet ==============" ); HashSet<Tiger> hs3 = new HashSet <Tiger>(); hs3.add(new Tiger (2 )); hs3.add(new Tiger (1 )); hs3.add(new Tiger (3 )); hs3.add(new Tiger (5 )); hs3.add(new Tiger (4 )); hs3.add(new Tiger (4 )); System.out.println(hs3.size()); System.out.println("========================" ); LinkedHashSet<Tiger> lhs3= new LinkedHashSet <Tiger>(); lhs3.add(new Tiger (2 )); lhs3.add(new Tiger (1 )); lhs3.add(new Tiger (3 )); lhs3.add(new Tiger (5 )); lhs3.add(new Tiger (4 )); lhs3.add(new Tiger (4 )); System.out.println(lhs3.size()); } }
Cat类本身没有hashCode(),而是继承Object类,而Object的hashCode()会返回对象信息和内存地址经过运算后的一个int值。两个不同的Cat(4)对象,它们的hashCode()返回值是不同的。
Dog类本身改写了hashCode()方法,其返回值是具体的size。所以两个不同Dog(4),它们的hashCode()返回值是相同的。
这三个方法三位一体:equals()是相同的;hashCode()是相同的;toString()也应该是相同的。
ObjectTreeSetTest.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 import java.util.TreeSet;public class ObjectTreeSetTest { public static void main (String[] args) { System.out.println("==========Tiger TreeSet ==============" ); TreeSet<Tiger> ts3 = new TreeSet <Tiger>(); ts3.add(new Tiger (2 )); ts3.add(new Tiger (1 )); ts3.add(new Tiger (3 )); ts3.add(new Tiger (5 )); ts3.add(new Tiger (4 )); ts3.add(new Tiger (4 )); System.out.println(ts3.size()); } }
Map
定义:两个集合之间的元素对应关系。
一个输入对应到一个输出。
{Key, Value} 键值对,K-V对
Hashtable 同步,慢,数据量小
HashMap 不支持同步,快,数据量大
Properties 同步,文件形式,数据量小
Hashtable
K-V对,K和V都不允许为null
同步,多线程安全
无序的
适合小数据量
主要方法:clear, contains/containsValue, containsKey, get, put, remove, size
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 import java.util.Enumeration;import java.util.Hashtable;import java.util.Iterator;import java.util.Map;import java.util.Map.Entry;public class HashtableTest { public static void main (String[] args) { Hashtable<Integer,String> ht =new Hashtable <Integer,String>(); ht.put(1000 , "aaa" ); ht.put(2 , "bbb" ); ht.put(30000 , "ccc" ); System.out.println(ht.contains("aaa" )); System.out.println(ht.containsValue("aaa" )); System.out.println(ht.containsKey(30000 )); System.out.println(ht.get(30000 )); ht.put(30000 , "ddd" ); System.out.println(ht.get(30000 )); ht.remove(2 ); System.out.println("size: " + ht.size()); ht.clear(); System.out.println("size: " + ht.size()); Hashtable<Integer,String> ht2 =new Hashtable <Integer,String>(); for (int i=0 ;i<100000 ;i++) { ht2.put(i, "aaa" ); } traverseByEntry(ht2); traverseByKeySet(ht2); traverseByKeyEnumeration(ht2); } public static void traverseByEntry (Hashtable<Integer,String> ht) { long startTime = System.nanoTime(); System.out.println("============Entry迭代器遍历==============" ); Integer key; String value; Iterator<Entry<Integer, String>> iter = ht.entrySet().iterator(); while (iter.hasNext()) { Map.Entry<Integer, String> entry = iter.next(); key = entry.getKey(); value = entry.getValue(); } long endTime = System.nanoTime(); long duration = endTime - startTime; System.out.println(duration + "纳秒" ); } public static void traverseByKeySet (Hashtable<Integer,String> ht) { long startTime = System.nanoTime(); System.out.println("============KeySet迭代器遍历==============" ); Integer key; String value; Iterator<Integer> iter = ht.keySet().iterator(); while (iter.hasNext()) { key = iter.next(); value = ht.get(key); } long endTime = System.nanoTime(); long duration = endTime - startTime; System.out.println(duration + "纳秒" ); } public static void traverseByKeyEnumeration (Hashtable<Integer,String> ht) { long startTime = System.nanoTime(); System.out.println("============KeyEnumeration迭代器遍历==============" ); Integer key; String value; Enumeration<Integer> keys = ht.keys(); while (keys.hasMoreElements()) { key = keys.nextElement(); value = ht.get(key); } long endTime = System.nanoTime(); long duration = endTime - startTime; System.out.println(duration + "纳秒" ); } }
运行结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 true true true ccc ddd size: 2 size: 0 ============Entry迭代器遍历============== 11238700纳秒 ============KeySet迭代器遍历============== 10546900纳秒 ============KeyEnumeration迭代器遍历============== 11115100纳秒
HashMap
K-V对,K和V都运行为null
不同步,多线程不安全
Map m = Collections.synchronizedMap(new HashMap(…));
无序的
主要方法:clear, containsValue, containsKey, get, put, remove, size
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 import java.util.Enumeration;import java.util.HashMap;import java.util.Iterator;import java.util.Map;import java.util.Map.Entry;public class HashMapTest { public static void main (String[] args) { HashMap<Integer,String> hm =new HashMap <Integer,String>(); hm.put(1 , null ); hm.put(null , "abc" ); hm.put(1000 , "aaa" ); hm.put(2 , "bbb" ); hm.put(30000 , "ccc" ); System.out.println(hm.containsValue("aaa" )); System.out.println(hm.containsKey(30000 )); System.out.println(hm.get(30000 )); hm.put(30000 , "ddd" ); System.out.println(hm.get(30000 )); hm.remove(2 ); System.out.println("size: " + hm.size()); hm.clear(); System.out.println("size: " + hm.size()); HashMap<Integer,String> hm2 =new HashMap <Integer,String>(); for (int i=0 ;i<100000 ;i++) { hm2.put(i, "aaa" ); } traverseByEntry(hm2); traverseByKeySet(hm2); } public static void traverseByEntry (HashMap<Integer,String> ht) { long startTime = System.nanoTime(); System.out.println("============Entry迭代器遍历==============" ); Integer key; String value; Iterator<Entry<Integer, String>> iter = ht.entrySet().iterator(); while (iter.hasNext()) { Map.Entry<Integer, String> entry = iter.next(); key = entry.getKey(); value = entry.getValue(); } long endTime = System.nanoTime(); long duration = endTime - startTime; System.out.println(duration + "纳秒" ); } public static void traverseByKeySet (HashMap<Integer,String> ht) { long startTime = System.nanoTime(); System.out.println("============KeySet迭代器遍历==============" ); Integer key; String value; Iterator<Integer> iter = ht.keySet().iterator(); while (iter.hasNext()) { key = iter.next(); value = ht.get(key); } long endTime = System.nanoTime(); long duration = endTime - startTime; System.out.println(duration + "纳秒" ); } }
运行结果
1 2 3 4 5 6 7 8 9 10 true true ccc ddd size: 4 size: 0 ============Entry迭代器遍历============== 13389400纳秒 ============KeySet迭代器遍历============== 12616200纳秒
LinkedHashMap
基于双向链表的维持插入顺序的HashMap
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 import java.util.LinkedHashMap;import java.util.Iterator;import java.util.Map;import java.util.Map.Entry;public class LinkedHashMapTest { public static void main (String[] args) { LinkedHashMap<Integer,String> hm =new LinkedHashMap <Integer,String>(); hm.put(1 , null ); hm.put(null , "abc" ); hm.put(1000 , "aaa" ); hm.put(2 , "bbb" ); hm.put(30000 , "ccc" ); System.out.println(hm.containsValue("aaa" )); System.out.println(hm.containsKey(30000 )); System.out.println(hm.get(30000 )); hm.put(30000 , "ddd" ); System.out.println(hm.get(30000 )); hm.remove(2 ); System.out.println("size: " + hm.size()); System.out.println("遍历开始==================" ); Integer key; String value; Iterator<Entry<Integer, String>> iter = hm.entrySet().iterator(); while (iter.hasNext()) { Map.Entry<Integer, String> entry = iter.next(); key = entry.getKey(); value = entry.getValue(); System.out.println("Key:" + key + ", Value:" + value); } System.out.println("遍历结束==================" ); LinkedHashMap<Integer,String> hm2 =new LinkedHashMap <Integer,String>(); for (int i=0 ;i<100000 ;i++) { hm2.put(i, "aaa" ); } traverseByEntry(hm2); traverseByKeySet(hm2); } public static void traverseByEntry (LinkedHashMap<Integer,String> ht) { long startTime = System.nanoTime(); System.out.println("============Entry迭代器遍历==============" ); Integer key; String value; Iterator<Entry<Integer, String>> iter = ht.entrySet().iterator(); while (iter.hasNext()) { Map.Entry<Integer, String> entry = iter.next(); key = entry.getKey(); value = entry.getValue(); } long endTime = System.nanoTime(); long duration = endTime - startTime; System.out.println(duration + "纳秒" ); } public static void traverseByKeySet (LinkedHashMap<Integer,String> ht) { long startTime = System.nanoTime(); System.out.println("============KeySet迭代器遍历==============" ); Integer key; String value; Iterator<Integer> iter = ht.keySet().iterator(); while (iter.hasNext()) { key = iter.next(); value = ht.get(key); } long endTime = System.nanoTime(); long duration = endTime - startTime; System.out.println(duration + "纳秒" ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 true true ccc ddd size: 4 遍历开始================== Key:1, Value:null Key:null, Value:abc Key:1000, Value:aaa Key:30000, Value:ddd 遍历结束================== ============Entry迭代器遍历============== 9191900纳秒 ============KeySet迭代器遍历============== 9211800纳秒
HashMap遍历是无序的
LinkedHashMap遍历的顺序和它的插入的顺序保持一致
TreeMap遍历的顺序是按照大小或者compareTo方法规定的
TreeMap
基于红黑树的Map,可以根据key的自然排序或者compareTo方法进行排序输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 import java.util.TreeMap;import java.util.Iterator;import java.util.Map;import java.util.Map.Entry;public class TreeMapTest { public static void main (String[] args) { TreeMap<Integer,String> hm =new TreeMap <Integer,String>(); hm.put(1 , null ); hm.put(1000 , "aaa" ); hm.put(2 , "bbb" ); hm.put(30000 , "ccc" ); System.out.println(hm.containsValue("aaa" )); System.out.println(hm.containsKey(30000 )); System.out.println(hm.get(30000 )); hm.put(30000 , "ddd" ); System.out.println(hm.get(30000 )); System.out.println("size: " + hm.size()); System.out.println("遍历开始==================" ); Integer key; String value; Iterator<Entry<Integer, String>> iter = hm.entrySet().iterator(); while (iter.hasNext()) { Map.Entry<Integer, String> entry = iter.next(); key = entry.getKey(); value = entry.getValue(); System.out.println("Key:" + key + ", Value:" + value); } System.out.println("遍历结束==================" ); TreeMap<Integer,String> hm2 =new TreeMap <Integer,String>(); for (int i=0 ;i<100000 ;i++) { hm2.put(i, "aaa" ); } traverseByEntry(hm2); traverseByKeySet(hm2); } public static void traverseByEntry (TreeMap<Integer,String> ht) { long startTime = System.nanoTime(); System.out.println("============Entry迭代器遍历==============" ); Integer key; String value; Iterator<Entry<Integer, String>> iter = ht.entrySet().iterator(); while (iter.hasNext()) { Map.Entry<Integer, String> entry = iter.next(); key = entry.getKey(); value = entry.getValue(); } long endTime = System.nanoTime(); long duration = endTime - startTime; System.out.println(duration + "纳秒" ); } public static void traverseByKeySet (TreeMap<Integer,String> ht) { long startTime = System.nanoTime(); System.out.println("============KeySet迭代器遍历==============" ); Integer key; String value; Iterator<Integer> iter = ht.keySet().iterator(); while (iter.hasNext()) { key = iter.next(); value = ht.get(key); } long endTime = System.nanoTime(); long duration = endTime - startTime; System.out.println(duration + "纳秒" ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 true true ccc ddd size: 4 遍历开始================== Key:1, Value:null Key:2, Value:bbb Key:1000, Value:aaa Key:30000, Value:ddd 遍历结束================== ============Entry迭代器遍历============== 12081400纳秒 ============KeySet迭代器遍历============== 19865000纳秒
Properties
继承于Hashtable
可以将K-V对保存在文件中
适用于数据量少的配置文件
继承自Hashtable的方法: clear, contains/containsValue, containsKey, get, put, remove, size
从文件加载的load方法,写入到文件中的store方法
获取属性getProperty,设置属性setProperty
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 import java.io.BufferedInputStream;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.util.Enumeration;import java.util.Properties;public class PropertiesTest { public static String GetValueByKey (String filePath, String key) { Properties pps = new Properties (); try { InputStream in = new BufferedInputStream (new FileInputStream (filePath)); pps.load(in); String value = pps.getProperty(key); return value; }catch (IOException e) { e.printStackTrace(); return null ; } } public static void GetAllProperties (String filePath) throws IOException { Properties pps = new Properties (); InputStream in = new BufferedInputStream (new FileInputStream (filePath)); pps.load(in); Enumeration en = pps.propertyNames(); while (en.hasMoreElements()) { String strKey = (String) en.nextElement(); String strValue = pps.getProperty(strKey); } } public static void WriteProperties (String filePath, String pKey, String pValue) throws IOException { File file = new File (filePath); if (!file.exists()) { file.createNewFile(); } Properties pps = new Properties (); InputStream in = new FileInputStream (filePath); pps.load(in); OutputStream out = new FileOutputStream (filePath); pps.setProperty(pKey, pValue); pps.store(out, "Update " + pKey + " name" ); out.close(); } public static void main (String [] args) throws IOException{ System.out.println("写入Test.properties================" ); WriteProperties("Test.properties" ,"name" , "12345" ); System.out.println("加载Test.properties================" ); GetAllProperties("Test.properties" ); System.out.println("从Test.properties加载================" ); String value = GetValueByKey("Test.properties" , "name" ); System.out.println("name is " + value); } }
运行结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 true true ccc ddd size: 4 遍历开始================== Key:1, Value:null Key:2, Value:bbb Key:1000, Value:aaa Key:30000, Value:ddd 遍历结束================== ============Entry迭代器遍历============== 13335400纳秒 ============KeySet迭代器遍历============== 20651700纳秒
工具类
JCF中工具类
不存储数据,而是在数据容器上,实现高效操作
排序
搜索
Arrays类
排序:对数组排序,sort/parallelSort
查找:从数组中查找一个元素,binarySearch
批量拷贝:从源数组批量复制元素到目标数组,copyOf。
批量赋值:对数组进行批量赋值,fill。
等价性比较:判定两个数组内容是否相同,equals。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 import java.util.Arrays;import java.util.Random;public class ArraysTest { public static void main (String[] args) { testSort(); testSearch(); testCopy(); testFill(); testEquality(); } public static void testSort () { Random r = new Random (); int [] a = new int [10 ]; for (int i=0 ;i<a.length;i++) { a[i] = r.nextInt(); } System.out.println("===============测试排序================" ); System.out.println("排序前" ); for (int i=0 ;i<a.length;i++) { System.out.print(a[i] + "," ); } System.out.println(); System.out.println("排序后" ); Arrays.sort(a); for (int i=0 ;i<a.length;i++) { System.out.print(a[i] + "," ); } System.out.println(); } public static void testSearch () { Random r = new Random (); int [] a = new int [10 ]; for (int i=0 ;i<a.length;i++) { a[i] = r.nextInt(); } a[a.length-1 ] = 10000 ; System.out.println("===========测试查找============" ); System.out.println("10000 的位置是" + Arrays.binarySearch(a, 10000 )); } public static void testCopy () { Random r = new Random (); int [] a = new int [10 ]; for (int i=0 ;i<a.length;i++) { a[i] = r.nextInt(); } int [] b = Arrays.copyOf(a, 5 ); System.out.println("===========测试拷贝前五个元素============" ); System.out.print("源数组:" ); for (int i=0 ;i<a.length;i++) { System.out.print(a[i] + "," ); } System.out.println(); System.out.print("目标数组:" ); for (int i=0 ;i<b.length;i++) { System.out.print(b[i] + "," ); } System.out.println(); } public static void testFill () { int [] a = new int [10 ]; Arrays.fill(a, 100 ); Arrays.fill(a, 2 , 8 , 200 ); System.out.println("===========测试批量赋值============" ); System.out.print("数组赋值后:" ); for (int i=0 ;i<a.length;i++) { System.out.print(a[i] + "," ); } System.out.println(); } public static void testEquality () { int [] a = new int [10 ]; Arrays.fill(a, 100 ); int [] b = new int [10 ]; Arrays.fill(b, 100 ); System.out.println(Arrays.equals(a, b)); b[9 ] = 200 ; System.out.println(Arrays.equals(a, b)); } }
Collections类
处理对象是Collection及其子类
排序:对List进行排序,sort
搜索:从List中搜索元素,binarySearch
批量赋值:对List批量赋值,fill
最大、最小:查找集合中最大/小值,max,min
反序:将List反序排列,reverse
对象比较
对象实现Comparable接口(需要修改对象类)
compareTo方法
“>” 返回1,“==“返回0,”<” 返回-1
Arrays和Collections在进行对象sort时,自动调用该方法
新建Comparator(适用于对象类不可更改的情况)
compare方法
“>” 返回1,“==“返回0,”<” 返回-1
Comparator比较强将作为参数提交给工具类的sort方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 import java.util.Arrays;public class Person implements Comparable <Person> { String name; int age; public String getName () { return name; } public int getAge () { return age; } public Person (String name, int age) { this .name = name; this .age = age; } public int compareTo (Person another) { int i = 0 ; i = name.compareTo(another.name); if (i == 0 ) { return age - another.age; } else { return i; } } public static void main (String... a) { Person[] ps = new Person [3 ]; ps[0 ] = new Person ("Tom" , 20 ); ps[1 ] = new Person ("Mike" , 18 ); ps[2 ] = new Person ("Mike" , 20 ); Arrays.sort(ps); for (Person p : ps) { System.out.println(p.getName() + "," + p.getAge()); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class Person2 { private String name; private int age; public String getName () { return name; } public int getAge () { return age; } public Person2 (String name, int age) { this .name = name; this .age = age; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import java.util.Arrays;import java.util.Comparator;public class Person2Comparator implements Comparator <Person2> { public int compare (Person2 one, Person2 another) { int i = 0 ; i = one.getName().compareTo(another.getName()); if (i == 0 ) { return one.getAge() - another.getAge(); } else { return i; } } public static void main (String[] args) { Person2[] ps = new Person2 [3 ]; ps[0 ] = new Person2 ("Tom" , 20 ); ps[1 ] = new Person2 ("Mike" , 18 ); ps[2 ] = new Person2 ("Mike" , 20 ); Arrays.sort(ps, new Person2Comparator ()); for (Person2 p : ps) { System.out.println(p.getName() + "," + p.getAge()); } } }
运行结果
文件读写
文件系统及Java文件基本操作
文件系统是由OS(操作系统)管理的
文件系统和Java进程是平行的,是两套系统
文件系统是由文件夹和文件递归组合而成
文件目录分隔符
Linux/Unix 用/隔开
Windows用\隔开,涉及到转义,在程序中需用/或\代替
文件包括文件里面的内容和文件基本属性
文件基本属性:名称、大小、扩展名、修改时间等
文件类File
java.io.File是文件和目录的重要类(JDK6及以前是唯一)
目录也使用File类进行表示
File类与OS无关,但会受到OS的权限限制
常用方法
createNewFile,delete,exists, getAbsolutePath, getName, getParent,getPath, isDirectory, isFile, length, listFiles, mkdir, mkdirs
注意:File不涉及到具体的文件内容,只涉及属性