详尽笔记,用于复习和速查
将包含基础、进阶、高阶
目前更新到数据结构-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
上移到最高
打开cmdjava -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…
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
8public static void main(String[] args)
{
//PSVM,main函数
}
public void main()
{
//普通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
4byte a = (byte) -129;
System.out.println(a);//127
byte b = (byte) 128;
System.out.println(b);//-128怎么理解:
标准值:-128~127
两边可以延伸
左延伸-384(-128)~-129(127)
右延伸128(-128)~383(127)short/int/long 短整数/整数/长整数
short 16位 2个字节 -32768~32767,-2^15~2^15-1,默认值0
int 32位,4个字节 -2147483648~2147483647,-2^31~2^31-1,默认值0
long 64位 8个字节 -9223372036854775808~9223372036854775807,-2^63~2^63-1,默认值0Lfloat/double 浮点数
float 单精度 32位 4个字节 1.410^-45~3.410^38 默认值0.0f
double 双精度 64位 8个字节 4.910^-324~1.710^308 默认值0.0d
float和double都不能用来表示很精确的数字char 字符
16位 Unicode字符
最小值是\u0000
(即为0)
最大值是\uffff
(即为65535)
(\u4e00
~\u9fa5
两万多汉字)
运算符
>>
右移 除以2<<
左移 乘以2
自定义函数
重载(overload):同一个类中,函数名称可以相同,但函数参数的个数或者类型必须有所不同
不能以返回值来区分同名的函数
面向对象
对象=属性+方法
对象的规范=属性定义+方法定义1
A obj = new A();
- 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
- this负责指向本类中的成员变量
1
2
3
4
5
6
7
8
9
10
11
12public class Test{
private int id;
public Test(int id){
//在这个构造函数里,形参优先级更高
this.id = id;
}
public int getId(){
return id;
}
} - this负责指向本类中的成员方法
1
this.add(1,2);//调用本类的add方法,this可忽略
- this可代替本类的构造函数
1
this(5);//调用本类的一个形参的构造函数
继承、接口和抽象类
继承
父类/基类/超类
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
4public interface Animal{
public void eat();
public void move();
} - 类只可以继承(extends)一个类,但可实现(implements)多个接口,继承和实现可以同时
- 接口不算类,或者说是特殊的类
- 接口可以继承(多个)接口,没有实现的方法将会叠加
- 类实现接口,就必须实现所有未实现的方法。若没有全部实现,只能成为一个抽象类
- 接口里可定义变量,但一般是常量 (经指正,成员变量只能是常量)
总结
抽象类和接口相同点:两者都不能被实例化,不能new操作
不同点:
- 抽象类abstract,接口interface
- 抽象类可有部分方法实现,接口所有方法不能有实现 (经指正,Java8后,接口可有默认实现)
- 一个类只能继承(extends)一个(抽象)类,实现(implenments)多个接口
- 接口可继承(extends)多个接口
- 抽象类有构造函数,接口没有构造函数
- 抽象类可有main,也能运行,接口没有main函数
- 抽象类方法可有private/protected,接口方法都是public
类转型
类型可相互转型,但只限制于有继承关系的类
- 子类可转换成父类(从大到小,向上转型),而父类不可转为子类(从小变大,向下转型)
- 父类转为子类有一种情况例外:这个父类本身就是从子类转化而来
1
2Human obj1 = new Man();//OK, Man extends Human
Man obj2 = (Man) obj1;//OK, because obj1 is born from Man class
多态
- 类型转换带来的作用就是多态
- 子类继承父类的所有方法,但子类可重新定义一个名字,参数和父类样的方法,这种行为是重写(覆写,覆盖,overwrite/override,not overload(重载))
- 子类方法优先级高于父类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public 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();//call Man.eat()
Human obj2 = (Human) obj1;
obj2.eat();//call Man.eat()
Man obj3 = (Man) obj2;
obj3.eat();//call Man.eat()
}
}
契约设计:类不会直接使用另外一个类,而是采用接口的形式,外部可以“空投”这个接口下的任意子类对象
static、final和常量设计
static
static关键字可作用在:
变量
static变量只依赖于类存在(通过类即可访问),不依赖于对象实例存在。
所有对象实例,关于同一个变量的值都共享存储在一个共同的空间(栈)。方法
静态方法无需通过对象来引用,而通过类名可直接引用。
静态方法中只能使用静态变量,不能使用非静态变量。
静态方法禁止引用非静态方法。类
- 匿名方法块
static块只在类第一次被加载时调用,在程序运行期间,代码只运行一次
执行顺序:static块 > 匿名块 > 构造函数1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17class 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关键字用来修饰
- 类
- 方法
字段
final的类,不能被继承(不能有子类)
- 父类中若有final方法,子类不能改写此方法
子类方法和父类方法在方法名和参数列表一样,就属于方法改写/重写/覆写,若参数列表不一样,就属于方法重载 - final的变量不能再次赋值
若是基本类型的变量,不能修改其值
若是对象实例,不能修改其指针(但可修改对象内部的值)1
2
3
4
5
6
7
8
9
10
11
12
13
14class 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
11Long l1 = -128L;
Long l2 = -128L;
System.out.println(String.valueOf(l1==l2));//true
Float f1 = 0.5f;
Float f2 = 0.5f;
System.out.println(String.valueOf(f1 == f2));//false
Double d1 = 0.5;
Double d2 = 0.5;
System.out.println(String.valueOf(d1 == d2));//falseJava为常量字符串都建立常量池缓存机制
基本类型的包装类和字符串有两种创建方式
- 常量式(字面量)复制创建,放在栈内存(将被常量化)
1
2Integer a = 10;
String b = "abc"; - new对象进行创建,放在堆内存(不会常量化)这两种创建方式导致创建的对象存放的位置不同
1
2Integer 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
25int i1 = 10;
Integer i2 = 10;//自动装箱
System.out.println(i1 == i2);//true
//自动拆箱 基本类型和包装类进行比较,包装类自动拆箱
Integer i3 = new Integer(10);
System.out.println(i1 == i3);//true
//自动拆箱 基本类型和包装类进行比较,包装类自动拆箱
System.out.println(i2 == i3);//false
//两个对象比较,比较其地址
//i2是常量,放在栈内存常量池中,i3是new出对象,放在堆内存中
Integer i4 = new Integer(5);
Integer i5 = new Integer(5);
System.out.println(i1 == (i4+i5));//true
System.out.println(i2 == (i4+i5));//true
System.out.println(i3 == (i4+i5));//true
//i4+i5操作将会使得i4,i5自动拆箱为基本类型并运算得到10
//基础类型10和对象相比,将会使对象自动拆箱,做基本类型比较
Integer i6 = i4 + i5;//+操作使得i4,i5自动拆箱,得到10,因此i6 == i2
System.out.println(i1 == i6);//true
System.out.println(i2 == i6);//true
System.out.println(i3 == i6);//false常量赋值(堆内存)和new创建(栈内存)不是同一个对象
- 编译器只会优化确定的字符串,并缓存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22String s0 = "abcdef";
String s1 = "abc";
String s2 = "abc";
String s3 = new String("abc");
String s4 = new String("abc");
System.out.println(s1 == s2);//true常量池
System.out.println(s1 == s3);//false一个栈内存,一个堆内存
System.out.println(s3 == s4);//false两个都是堆内存
String s5 = s1 + "def";//设计到变量,编译器不优化
String s6 = "abc" + "def";//都是常量,编译器会自动优化成abcdef
String s7 = "abc" + new String("def");//设计到new对象,编译器不优化
System.out.println(s5 == s6);//false
System.out.println(s6 == s7);//false
System.out.println(s0 == s6);//true
String s8 = s3 + "def";//涉及到new对象,编译器不优化
String s9 = s4 + "def";//设计到new对象,编译器不优化
String s10 = s3 + new String("def");//涉及到new对象,编译器不优化
System.out.println(s8 == s9);//false
System.out.println(s8 == s10);//false
System.out.println(s9 == s10);//false
不可变对象和字符串
不可变对象
- 不可变对象(Immutable Object)
一旦创建,该对象(状态/值)不能被更改
其内在的成员变量的值不能修改
如八个基本型别的包装类
String, BigInteger和BigDecimal等 可变对象(Mutable Object)
普通对象1
2
3
4
5String a = new String("abc");
String b = a;
System.out.println(b);//abc
a = "def";
System.out.println(b);//abc不可变对象是指值对象不再修改,即abc不会被修改,而指针(句柄/变量名)a的指向可以修改。
不可变对象,也是传指针(引用)
由于不可变,临时变量指向新内存,外部实参的指针不改动1
2
3
4
5
6
7public static void change(String b)
{
b = "def";
}
a = new String("abc");
change(a);
System.out.println(a);//abca->abc
b–^
change(a)后
a->abc
b->def如何创建不可变对象
immutable对象是不可改变,改变要clone/new一个对象进行修改
所有属性是final和private
不提供setter方法
类是final的,或所有方法都是final
勒种包含mutable对象,那么返回拷贝需要深度clone不可变对象(Immutable Object)优点
只读,线程安全
并发读,提高性能
可重复使用- 缺点
制造垃圾,浪费空间
字符串
不可变对象
字符串内容比较:equals方法
是否指向同一个对象:指针比较==
- 字符串加法使用SrringBuffer/StringBuilder类的append方法进行修改
1
2String a = "abc";
a = a + "def";//String不可修改,需新申请空间,效率差
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
25public 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);//5
System.out.println(b);//abc
System.out.println(c);//abcdef
}
package, import和classpath
package和import
- 若所有Java类都是放置在同一个目录下的,类之间相互调用无需显式声明调用
同一个目录下,两个类的名字不能相同
文件过多不方便查找 - Java支持多个目录放置Java,并通过package/import/classpath/jar等机制配合使用,可支持跨目录放置和调用Java类类全称cn.edu.pku.PackageExample,短名称PackageExample
1
2
3
4package cn.edu.pku;
public class 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
12package cn.edu.pku;
import cn.edu.pku.PackageExample;
//或import cn.edu.pku.*
//但不能是import cn.*
//若PackageExample和当前类在同一个目录,可省略import
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
2class A { }//default class
public class B{ }//public class
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(大浮点数)
大数类没有限制,可表示无穷大的数字尽量采用字符串对BigDecimal赋值,这样精度更准确。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18BigInteger 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
8System.out.println(new BigDecimal("2.3"));
System.out.println(new BigDecimal(2.3));//2.29999999999999
BigDecimal num1 = new BigDecimal("10");
BigDecimal num2 = new BigDecimal("3");
//需要指定位数,防止无限循环,或者包含在try-catch中
BigDecimal num3 = num1.divide(num2, 3, BigDecimal.ROUND_HALF_UP);
System.out.println(num3);
随机数类Random
nextInt() 返回一个随机Int
nextInt(int a) 返回一个[0,a)之间的随机int
nextDouble() 返回一个[0.0,1.0]之间double
ints方法批量返回随机数数组
Math.random() 返回一个[0.0,1.0]之间double1
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类随机生成
Random rd = new Random();
System.out.println(rd.nextInt());
System.out.println(rd.nextInt(100));//0-100的随机数
System.out.println(rd.nextLong());
System.out.println(rd.nextDouble());
//第二种,生成一个范围内的随机数 例如0-10之间的随机数
System.out.println(Math.round(Math.random()*10));
//JDK 8 新增方法
rd.ints();//返回无限个int类型范围内的数据
int[] arr = rd.ints(10).toArray();//生成10个int范围类的个数
for(int i = 0;i < arr.length; i++){
System.out.println(arr[i]);
}
arr = rd.ints(5, 10, 100).toArray();//5个10-100的随机数
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, valueOf1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24String a = "123;456,789,123 ";
System.out.println(a.charAt(0));//返回第0个元素
System.out.println(a.indexOf(";"));//返回第一个;的位置
System.out.println(a.concat(";000"));//连接一个新字符串并返回,a不变
System.out.println(a.contains("000"));//判断a是否包含000
System.out.println(a.endWith("000"));//判断a是否以000结尾
System.out.println(a.equals("000"));//判断是否等于000
System.out.println(a.equalsIgnoreCase("000"));//判断在忽略大小写情况下是否等于000
System.out.println(a.length());//返回a长度
System.out.println(a.trim());//返回a去除前后空格后的字符串,a不变
String[] b = a.split(";");//将a字符串按照;分割成数组
for(int i = 0; i < b.length; i++){
System.out.println(b[i]);
}
System.out.println(a.substring(2, 5));//截取a的第2个到第5个字符 a不变
System.out.println(a.replace("1", "a"));
System.out.println(a.replaceAll("1", "a");//replaceAll第一个参数是正则表达式
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
82Calendar 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(){
//假设求6月的最后一天
int currentMonth = 6;
//先求出7月份的第一天
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);
//2018.7.31
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);
//2018.8.31只改变天数,不改变月份
}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
LocalDate firstDay_2014 = LocalDate.of(2014, Month.JANUARY, 1);
//给定错误时间参数,将报异常java.time.DateTimeException
//LocalDate firstDay_2014 = LocalDate.of(2014, Month.FEBRUARY, 29);
//可更改时区
LocalDate todayBeijing = LocalDate.now(ZoneId.of("Asia/Shanghai"));
//从纪元日01/01/1970开始365天
LocalDate dateFromBase = LocalDate.ofEpochDay(365);
//2014年的第100天
LocalDate hundredDay2014 = LocalDate.ofYearDay(2014, 100);
//当前时间 时分秒 纳秒
LocalTime time = LocalTime.now();
//根据时分秒
LocalTime specificTime = LocalTime.of(12, 20, 25, 40);
//错误的时间参数 将报DateTimeException
//LocalTime invalidTime = LocalTime.of(25,20);
//上海时间
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());
//今天和01/01/2015比较
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());格式化(Format)相关类
- java.text包java.text.Format的子类
NumberFormat:数字格式化,抽象类
– DecimalFormat 工厂模式
例如:将1234567格式化输出为1,234,567MessageFormat:字符串格式化1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25DecimalFormat df1,df2;
//整数部分为0,#认为整数不存在,可不写;0认为没有,但至少写一位,写0
df1 = new DecimalFormat("#.00");
df2 = new DecimalFormat("0.00");
System.out.println(df1.format(0.1));//.10
System.out.println(df2.format(0.1));//0.10
df1 = new DecimalFormat("0.00");
df2 = new DecimalFormat("0.##");
System.out.println(df1.format(0.1));//0.10
System.out.println(df2.format(0.1));//0.1
df1 = new DecimalFormat("0.00");
df2 = new DecimalFormat("#.00");
System.out.println(df1.format(2));//2.00
System.out.println(df2.format(2));//2.00
System.out.println(df1.format(20));//20.00
System.out.println(df2.format(20));//20.00
System.out.println(df1.format(200));//200.00
System.out.println(df2.format(200));//200.00
支持多个参数-值对位复制文本S
支持变量的自定义格式
例如将“Hello {1}”根据变量值格式化为Hello World输出1
2
3
4
5
6
7String 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);DateFormat:日期/时间格式化,抽象类1
2ABCDEFGHIJKLMNOPQ
oh, 3.14 is a good number
– SimpleDateFormat 工厂模式
parse:将字符串格式化为时间对象
format:将时间对象格式化为字符串
如将当前时间转为YYYY-MM-DD HH24:MI:SS输出1
2
3
4
5
6
7
8
9
10
11
12
13
14String 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输出LocalDate类的月份是1-12,不用加1;Calendar类的月份是0-11,需要加1。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);
异常和异常处理
异常
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。3中,第一个异常执行完finally后再执行第二个异常。(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
43try
{
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");
}
进入catch块后,并不会返回到try发生异常的位置,也不会执行后续的catch块,一个异常只能进入一个catch块。
一般将小异常(具体的异常子类,如ArithmeticException)写在前面,大(宽泛)的异常(如Exception)写在末尾。
允许包含:1
2
3
4
5
6
7
8
9try{
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
26public 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
{
//ArithmeticException is a RuntimeException, not checked exception
public int divide(int x, int y) throws ArithmeticException
{
int result = x/y;
return x/y;
}
} - 一个方法被覆盖,覆盖它的方法不限抛出相同的异常,或者异常的子类。
- 如果父类的方法抛出多个异常,那么重写的子类方法必须抛出那些异常的子集,也就是不能抛出新的异常。
Father.javaSon.java1
2
3
4
5
6public class Father{
public void f1() throws ArithmeticException
{
}
}1
2
3public 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在方法内部程序中,抛出异常采用throw关键字;在方法头部声明中,声明异常采用throws关键字。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
27public 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;
}
}
MyExceptionTest.java注释1:main函数里调用了一个声明MyException异常的方法,但没处理。因为MyException继承Exception,是属于非RuntimeException,也就是Checked Exception,因此编译器会检查到程序没处理而报错。解法:1)采用try-catch-finally结构 2)main函数也throws MyException。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16public class MyExceptionTest {
public static void testException() throws MyException {
throw new MyException("10001", "The reason of myException");
}
public static void main(String[] args) {
//MyExceptionTest.testException();注释1 要报错需注释掉下方代码
try {
MyExceptionTest.testException();
} catch (MyException e) {
e.printStackTrace();
System.out.println("returnCode:" + e.getReturnCode());
System.out.println("returnCode:" + e.getReturnMsg());
}
}
}
DivideByMinusException.javaStudent.java1
2
3
4
5
6
7
8
9
10
11
12public class DivideByMinusException extends Exception {
int divisor;
public DivideByMinusException(String msg, int divisor)
{
super(msg);
this.divisor = divisor;
}
public int getDevisor()
{
return this.getDevisor();
}
}对于CheckedException,编译器要求调用者处理,要么采用try-catch-finally处理,要么采用throws声明异常。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
68public 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.divide2(5,0);
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//unchecked exception
{
return x/y;
}
public int divide4(int x, int y)
{
// try
// {
// return divide3(x,y);
// }
// catch(ArithmeticException ex)
// {
// ex.printStackTrace();
// return 0;
// }
return divide3(x,y);//尽管divide3报告异常,但divide4无需处理。
//若调用divide5(x,y);就需要做try-catch处理。
}
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;
}
}
}
数据结构
数组
数组是一个存放多个数据的容器
数据是同一种类型
所有数据是线性规则排列
可通过位置索引来快速定位访问数据
需明确容器的长度Java数组定义和初始化
1
2
3
4
5
6
7
8
9
10
11
12
13int a[];//还没有new操作,实际上是null,也不知道内存位置
int[] b;
int[] c = new int[2];//c有2个元素,都是0
c[0] = 1, c[1] = 2;//初始化
int d[] = new int[]{1,2,3};//同时定义和初始化
int d1[] = {1,2,3};//同时定义和初始化
//声明变量时没有分配内存,不需要指定大小,以下是错误的
//int a[2];
//int[2] b;
//int[2] c = new int[2];
//int d[2] = new int[2];数组索引
数组的length属性标识数组的长度
从0开始,到length-11
int[] a = new int[5];//a[0]~a[4],a.length是5
数组不能越界访问,否则会报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
17int 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, removeJCF主要的数据结构实现类
列表(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 | import java.util.ArrayList; |
结果表明:for循环遍历最快,随机索引值遍历和for差距不大,迭代器最慢。
- LinkedList
以双向链表实现的列表,不支持同步
可被当作堆栈、队列和双端队列进行操作
顺序访问高效、随机访问较差,中间插入和删除高效
适用于经常变化的数据
1 | import java.util.ArrayList; |
结果: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
70import 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>();
//ArrayList add
long startTime = System.nanoTime();
for(int i = 0; i < times; i++) {
arrayList.add(0, i);//add到头部
}
long endTime = System.namoTime();
long duration = endTime - startTime;
System.out.println(duration + "纳秒");
//LinkedList add
startTime = System.nanoTime();
for(int i = 0; i < times; i++) {
linkedList.add(0, i);
}
endTime = System.namoTime();
duration = endTime - startTime;
System.out.println(duration + "纳秒");
//ArrayList get
startTime = System.nanoTime();
for(int i = 0; i < times; i++) {
arrayList.get(i);
}
endTime = System.namoTime();
duration = endTime - startTime;
System.out.println(duration + "纳秒");
//LinkedList get
startTime = System.nanoTime();
for(int i = 0; i < times; i++) {
linkedList.get(i);
}
endTime = System.namoTime();
duration = endTime - startTime;
System.out.println(duration + "纳秒");
//ArrayList remove
startTime = System.nanoTime();
for(int i = 0; i < times; i++) {
arrayList.remove(0);
}
endTime = System.namoTime();
duration = endTime - startTime;
System.out.println(duration + "纳秒");
//LinkedList remove
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结果:for循环是最快的,位置索引第二名,Enumeration第三,迭代器最慢。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
72import 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 + "纳秒");
}
//for循环遍历
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 + "纳秒");
}
//Enumeration遍历
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 + "纳秒");
}
}
Set
确定性:对任意对象都能判定其是否属于某一个集合
互异性:集合内每个元素都是不相同的,内容互异
无序性:集合内的顺序无关
集合接口Set
- HashSet 基于散列函数的集合,无序,不支持同步
基于HashMap实现的,可以容纳null元素,不支持同步
但以下方法可以实现同步add 添加一个元素1
Set s = Collections.synchronizedSet(new HashSet(...));
clear 清除整个HashSet
contains 判定是否包含一个元素
remove 删除一个元素 size 大小
retainAll 计算两个集合交集 - TreeSet 基于树结构的集合,可排序的,不支持同步
- LinkedHashSet 基于散列函数和双向链表的集合,可排序的,不支持同步
1 | import java.util.ArrayList; |
迭代器慢于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
68import 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); //3 重复
lhs.add(null); //null 重复
System.out.println(lhs.size()); //6
if(!lhs.contains(6))
{
lhs.add(6);
}
System.out.println(lhs.size()); //7
lhs.remove(4);
System.out.println(lhs.size()); //6
//lhs.clear();
//System.out.println(lhs.size()); //0
//for循环遍历
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循环遍历
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
69import 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(null); 错误,不支持null
ts.add(1000);
ts.add(20);
ts.add(3);
ts.add(40000);
ts.add(5000000);
ts.add(3); //3 重复
System.out.println(ts.size()); //5
if(!ts.contains(6))
{
ts.add(6);
}
System.out.println(ts.size()); //6
ts.remove(4);
System.out.println(ts.size()); //5
//lhs.clear();
//System.out.println(lhs.size()); //0
//for循环遍历
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循环遍历
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.javaCat.java1
2
3
4
5
6
7
8
9
10
11
12
13
14import 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()); //4
}
}Dog.java1
2
3
4
5
6
7
8
9
10
class Cat
{
private int size;
public Cat(int size)
{
this.size = size;
}
}Tiger.java1
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实现Comparable接口,所以必须实现compareTo方法来比较大小。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();
}
}
compareTo方法具体规则如下:若a>0,则obj1>obj2;1
int a = obj1.compareTo(obj2);
若a==0,则obj1==obj2;
若a<0,则obj1<obj2。
HashSet的元素判定规则只和hashCode、equals这2个方法有关,和compareTo方法无关。
Java的四个重要接口:
Comparable可比较的
Clonable可克隆的
Runnable可线程化的
Serializable可序列化的
ObjectHashSetTest.java1
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
74import 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()); //6
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()); //6
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()); //5
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()); //5
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()); //6
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()); //6
}
}
Cat类本身没有hashCode(),而是继承Object类,而Object的hashCode()会返回对象信息和内存地址经过运算后的一个int值。两个不同的Cat(4)对象,它们的hashCode()返回值是不同的。
Dog类本身改写了hashCode()方法,其返回值是具体的size。所以两个不同Dog(4),它们的hashCode()返回值是相同的。
这三个方法三位一体:equals()是相同的;hashCode()是相同的;toString()也应该是相同的。
ObjectTreeSetTest.java1
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
47import java.util.TreeSet;
public class ObjectTreeSetTest {
public static void main(String[] args) {
/*
System.out.println("==========Cat TreeSet ==============");
TreeSet<Cat> ts = new TreeSet<Cat>();
ts.add(new Cat(2));
ts.add(new Cat(1));
ts.add(new Cat(3));
ts.add(new Cat(5));
ts.add(new Cat(4));
ts.add(new Cat(4));
System.out.println(ts.size()); //5
System.out.println("==========Dog TreeSet ==============");
TreeSet<Dog> ts2 = new TreeSet<Dog>();
ts2.add(new Dog(2));
ts2.add(new Dog(1));
ts2.add(new Dog(3));
ts2.add(new Dog(5));
ts2.add(new Dog(4));
ts2.add(new Dog(4));
System.out.println(ts2.size()); //5
*/
//添加到TreeSet的,需要实现Comparable接口,即实现compareTo方法
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()); //5
}
}
Map
定义:两个集合之间的元素对应关系。
一个输入对应到一个输出。
{Key, Value} 键值对,K-V对
Hashtable 同步,慢,数据量小
HashMap 不支持同步,快,数据量大
Properties 同步,文件形式,数据量小
Hashtable
K-V对,K和V都不允许为null
同步,多线程安全
无序的
适合小数据量
主要方法:clear, contains/containsValue, containsKey, get, put, remove, size1
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
98import 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(1, null); 编译不报错 运行报错
//ht.put(null,1); 编译报错
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"); //更新覆盖ccc
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
key = entry.getKey();
// 获取value
value = entry.getValue();
//System.out.println("Key:" + key + ", Value:" + value);
}
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
value = ht.get(key);
//System.out.println("Key:" + key + ", Value:" + value);
}
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
value = ht.get(key);
//System.out.println("Key:" + key + ", Value:" + value);
}
long endTime = System.nanoTime();
long duration = endTime - startTime;
System.out.println(duration + "纳秒");
}
}运行结果:
1
2
3
4
5
6
7
8
9
10
11
12
13true
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 | import java.util.Enumeration; |
运行结果1
2
3
4
5
6
7
8
9
10true
true
ccc
ddd
size: 4
size: 0
============Entry迭代器遍历==============
13389400纳秒
============KeySet迭代器遍历==============
12616200纳秒
LinkedHashMap
基于双向链表的维持插入顺序的HashMap1
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
90import 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"); //更新覆盖ccc
System.out.println(hm.get(30000));
hm.remove(2);
System.out.println("size: " + hm.size());
//hm.clear();
//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
key = entry.getKey();
// 获取value
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
key = entry.getKey();
// 获取value
value = entry.getValue();
//System.out.println("Key:" + key + ", Value:" + value);
}
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
value = ht.get(key);
//System.out.println("Key:" + key + ", Value:" + value);
}
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
15true
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 | import java.util.TreeMap; |
1 | true |
- 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
77import 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;
//关于Properties类常用的操作
public class PropertiesTest {
//根据Key读取Value
public static String GetValueByKey(String filePath, String key) {
Properties pps = new Properties();
try {
InputStream in = new BufferedInputStream (new FileInputStream(filePath));
pps.load(in); //所有的K-V对都加载了
String value = pps.getProperty(key);
//System.out.println(key + " = " + value);
return value;
}catch (IOException e) {
e.printStackTrace();
return null;
}
}
//读取Properties的全部信息
public static void GetAllProperties(String filePath) throws IOException {
Properties pps = new Properties();
InputStream in = new BufferedInputStream(new FileInputStream(filePath));
pps.load(in); //所有的K-V对都加载了
Enumeration en = pps.propertyNames(); //得到配置文件的名字
while(en.hasMoreElements()) {
String strKey = (String) en.nextElement();
String strValue = pps.getProperty(strKey);
//System.out.println(strKey + "=" + strValue);
}
}
//写入Properties信息
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);
//调用 Hashtable 的方法 put。使用 getProperty 方法提供并行性。
//强制要求为属性的键和值使用字符串。返回值是 Hashtable 调用 put 的结果。
OutputStream out = new FileOutputStream(filePath);
pps.setProperty(pKey, pValue);
//以适合使用 load 方法加载到 Properties 表中的格式,
//将此 Properties 表中的属性列表(键和元素对)写入输出流
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
15true
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
87import 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
42import 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
28import 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) {
// TODO Auto-generated method stub
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());
}
}
}1
2
3Mike,18
Mike,20
Tom,20
文件读写
文件系统及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不涉及到具体的文件内容,只涉及属性