集合
一、概述
- 集合、数组都是对多个数据进行存储操作的结构,称为Java容器。
- 数组在存储在多个数据的特点:
- 一旦初始化以后,其长度就确定了
- 数组一旦定义好,其元素的类型也就确定了,只能操作指定类型的数据。
- 数组在存储多个数据的缺点:
- 一旦初始化以后,长度不可以修改。
- 数组中提供的方法有限,对于添加、删除、操作数据等操作,非常不方便,同时效率不高。
- 获取数组中实际元素的个数,数组中没有现成的数据或方法可用。
- 数组存储数据的特点:有序、可重复。对于无序、不可重复的需求,不能满足。
二、集合框架
集合可分为Collection和Map两种体系
- Collection接口:单列数据,定义了存取一组对象的方法的集合
- List接口:元素有序、可重复的集合。--动态数组
ArrayList
LinkedList
Vector
- Set接口:元素无序、不可重复的集合。
HashSet
:作为Set
接口的主要实现类:线程不安全的,可以存储null
值。
LinkedHashSet
:HashSet
的子类,遍历其内部数据时,可以按照添加的顺序遍历。
TreeSet
:底层为红黑树,可以按照添加对象的指定属性进行排序。
- Map接口:双列数据,具有保存映射关系(key-value)的集合
HashMap
:作为Map
的主要实现类,线程不安全的,效率高;可以存储null
的key-value
;
LinkedHashMap
:保证在遍历Map
元素时,可以按照添加的顺序遍历(在原有的HashMap
底层结构基础上,添加了一对指针,指向前一个和后一个元素),对于频繁的遍历操作效率高于HashMap
TreeMap
:可以按照添加的key-value
进行排序,实现排序遍历,此时考虑key
的自然排序和定制排序;底层使用红黑树。
Hashtable
:作为古老的实现类,线程安全的,效率低;不能存储null
的key和value;
Properties
:常用来处理配置文件;key-value
都是String
类型.
三、Collection
1. 常用方法
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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124 | //Collection接口的常用方法
import java.util.*;
public class CollectionTest {
public static void main(String[] args) {
Collection collection1 = new ArrayList();
Collection collection2 = new ArrayList();
// 添加元素
collection1.add("ABC");
collection2.add(123);
// 获取添加元素的个数
System.out.println(collection1.size());
// 将collection2中的元素添加到collection1
collection1.addAll(collection2);
System.out.println(collection1); // [ABC, 123]
// 判断当前集合是否为空
System.out.println(collection1.isEmpty()); // false
// 清空集合元素
collection2.clear();
// 判断当前集合是否包含obj
// 判断时会调用对象所在类的equals()方法进行比较
boolean contains = collection1.contains("D");
System.out.println(contains); // false
// ** 向Collection接口的实现类的对象中添加数据obj时,要求obj所在类重写equals()
collection1.add(new String("Hello"));
System.out.println(collection1.contains(new String("Hello"))); // true
collection1.add(new Person("Tom", 18));
System.out.println(collection1.contains(new Person("Tom", 18))); // true
// 判断collection3中的所有元素是否都在collection1中。
Collection collection3 = Arrays.asList("ABC", 123);
boolean containsAll = collection1.containsAll(collection3);
System.out.println(containsAll); // true
// 删除,删除成功返回true,没找到返回false
boolean remove = collection1.remove(123);
System.out.println(remove);
System.out.println(collection1); // [ABC, Hello, Person{name='Tom', age=18}]
// 删除collection3中所有的元素
collection1.removeAll(collection3);
System.out.println(collection1); // [Hello, Person{name='Tom', age=18}]
// 获取collection4和collection1的交集
Collection collection4 = Arrays.asList("Hello");
collection1.retainAll(collection4);
System.out.println(collection1); // [Hello]
// 判断当前集合和形参集合元素相同
Collection collection5 = Arrays.asList("Hello");
System.out.println(collection1.equals(collection5)); // true
//hashCode() 返回当前对象的Hash值
System.out.println(collection1.hashCode()); // 69609681
collection1.add(123);
// 集合-->数组
Object[] arr = collection1.toArray();
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
// 数组-->集合
List<String> list1 = Arrays.asList(new String[]{"AA", "BB", "CC"});
System.out.println(list1); // [A, B, C]
List list2 = Arrays.asList(new int[]{1, 2, 3});
System.out.println(list2); // [[I@1b6d3586]
List list3 = Arrays.asList(1, 2, 3);
System.out.println(list3); // [1, 2, 3]
}
}
class Person {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age && Objects.equals(name, person.name);
}
}
|
2. 集合元素的遍历
2.1 hasNext()
和next()
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 | // Iterator用来遍历Collection对象
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
public class IteratorTest {
public static void main(String[] args) {
Collection collection = Arrays.asList("A", "B", 123, "Hello");
Iterator iterator = collection.iterator();
// 方式一
// System.out.println(iterator.next()); // A
// System.out.println(iterator.next()); // B
// System.out.println(iterator.next()); // 123
// System.out.println(iterator.next()); // Hello
// System.out.println(iterator.next()); // NoSuchElementException
// 方式二
// for (int i = 0; i < collection.size(); i++) {
// System.out.println(iterator.next());
// }
// 方式三 推荐
// hasNext()判断是否还有下一个元素
while (iterator.hasNext()){
// next() ①指针下移②将下移以后集合位置上的元素返回
System.out.println(iterator.next());
}
// 错误方式一
// while ((iterator.next()) != null){
// System.out.println(iterator.next());
// }
// 错误方式二
// 集合对象每次调用iterator()方法都会得到一个全新的迭代器对象,默认游标都在集合的第一个元素之前
// while (collection.iterator().hasNext()){
// System.out.println(collection.iterator().next()); // 死循环输出A
// }
}
}
|
2.2 remove()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 | import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class IteratorTest {
public static void main(String[] args) {
Collection collection = new ArrayList();
collection.add("A");
collection.add("123");
collection.add("Hello");
Iterator iterator = collection.iterator();
while (iterator.hasNext()) {
Object obj = iterator.next();
if ("Hello".equals(obj)) {
iterator.remove();
}
}
iterator = collection.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
|
3. List
接口
1. ArrayList
、 LinkedList
和 Vector
三者的异同
- 相同点:
- 都实现了
List
接口,存储数据的特点相同(存储有序的、可重复的数据)
- 不同点
ArrayList
作为List
接口的主要实现类。线程不安全的,效率高。底层用Object[] elementData
存储
Vector
是List
接口古老的实现类。线程安全的,效率低。底层用Object[] elementData
存储
LinkedList
底层使用双向链表存储。对于频繁的插入和删除操作,效率比ArrayList
高。
2. 源码分析
2.1 ArrayList
源码分析
2.2 LinkedList
源码分析
2.3 Vector
源码分析
3. 常用方法
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 | import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
public class ListTest {
public static void main(String[] args) {
ArrayList list = new ArrayList();
// add()
list.add("A");
list.add("Hello");
list.add(123);
System.out.println(list); // [A, Hello, 123]
list.add(2, "Hello");
System.out.println(list); // [A, Hello, Hello, 123]
// addAll()
Collection<Integer> integerList = Arrays.asList(1, 2, 3);
list.addAll(integerList);
System.out.println(list); // [A, Hello, Hello, 123, 1, 2, 3]
// indexOf() 返回obj在集合中首次出现的索引位置,不存在返回-1
System.out.println(list.indexOf("Hello"));
// lastIndexOf() 返回obj在集合中末次出现的索引位置,不存在返回-1
System.out.println(list.lastIndexOf("Hello")); // 2
// remote() 返回删除的元素
Object obj = list.remove(1);
System.out.println(obj); // Hello
// set()
list.set(0, "B");
System.out.println(list); // [B, Hello, 123, 1, 2, 3]
// subList()
System.out.println(list.subList(3, 6)); // [1, 2, 3]
}
}
|
4. Set接口
1. 集合的特点
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 | import java.util.HashSet;
public class SetTest {
public static void main(String[] args) {
// 无序性:存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值决定的。
// 不可重复:保证添加的元素按照equals()判断时,不能返回true,即相同的元素只能添加一个。
HashSet set = new HashSet();
set.add(5);
set.add(1);
set.add("B");
set.add(5);
System.out.println(set); // [1, B, 5]
// 添加元素的过程
// 向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a的哈希值值,
// 此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置(索引位置),判断
// 数组此位置上是否已经有元素
// 如果此位置上没有其他元素,则元素a添加成功 --> 情况1
// 如果此位置上右其他元素b(或者以链表形式存在的多个元素),则比较元素a和元素b的哈希值,
// 如果哈希值不相同,则元素a添加成功。 --> 情况2
// 如果哈希值相同,进行需要调用元素a所在类的equals()方法:
// 如果返回true,元素a添加失败
// 如果返回false,元素a添加成功 --> 情况3
// 对于添加成功的情况2和情况3而言: 元素a与已经存在指定索引位置上数据一链表的方式存储。
// jdk8中,原来的元素在数组中指向元素a
}
}
|
2. 重写hashCode()
和equals()
向Set
中添加的数据,其所在的类一定要重写hashCode()
和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 | import java.util.HashSet;
public class SetTest {
public static void main(String[] args) {
HashSet set = new HashSet();
set.add(5);
set.add(1);
set.add(new User("Tom", 18));
set.add(new User("Tom", 18));
System.out.println(set); // [1, 5, User{name='Tom', age=18}]
}
}
class User {
String name;
int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
if (age != user.age) return false;
return name != null ? name.equals(user.name) : user.name == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
return result;
}
}
|
3. LinkedHashSet
做为HashSet
的子类,在添加数据的同时,每个数据还维护了两个引用,记录此数据前一个数据和后一个数据。
对于频繁的遍历操作,效率高于HashSet
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | import java.util.Iterator;
import java.util.LinkedHashSet;
public class LinkedHashSetTest {
public static void main(String[] args) {
LinkedHashSet set = new LinkedHashSet();
set.add(5);
set.add(1);
set.add("A");
Iterator iterator = set.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
// 5
// 1
// A
}
}
|
4. TreeSet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 | import java.util.Iterator;
import java.util.TreeSet;
public class TreeSetTest {
public static void main(String[] args) {
// 向TreeSet中添加的数据,要求相同类的对象
TreeSet set = new TreeSet();
set.add(12);
set.add(13);
set.add(-3);
set.add(0);
Iterator iterator = set.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
// 按从小到大顺序排
}
// -3
// 0
// 12
// 13
}
}
|
4.1 自然排序
自然排序中,比较两个对象是否相同的标准为:compareTo()
返回0,不再是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 | import java.util.Iterator;
import java.util.TreeSet;
public class TreeSetTest {
public static void main(String[] args) {
TreeSet set = new TreeSet();
set.add(new User("Tom", 18));
set.add(new User("Jerry", 16));
set.add(new User("Jack", 32));
set.add(new User("Jack", 23));
Iterator iterator = set.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
class User implements Comparable {
String name;
int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
if (age != user.age) return false;
return name != null ? name.equals(user.name) : user.name == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
return result;
}
// 按照姓名从小到大排序,年龄从小到大排序
@Override
public int compareTo(Object o) {
if (o instanceof User) {
User user = (User) o;
int i = this.name.compareTo(user.name);
if (i != 0) {
return i;
} else {
return Integer.compare(this.age, user.age);
}
} else {
throw new RuntimeException("类型错误");
}
}
}
|
4.2 定制排序
定制排序中,比较两个对象是否相同的标准为:compare()
返回0,不再是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 | import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;
public class TreeSetTest {
public static void main(String[] args) {
// 定制排序
Comparator comparator = new Comparator(){
@Override
public int compare(Object o1, Object o2) {
if (o1 instanceof User && o2 instanceof User){
User u1 = (User) o1;
User u2 = (User) o2;
return Integer.compare(u1.getAge(), u2.getAge());
}else{
throw new RuntimeException("类型错误");
}
}
};
TreeSet set = new TreeSet(comparator);
set.add(new User("Tom", 18));
set.add(new User("Jerry", 16));
set.add(new User("Jack", 16));
set.add(new User("Jack", 23));
Iterator iterator = set.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
// User{name='Jerry', age=16}
// User{name='Tom', age=18}
// User{name='Jack', age=23}
}
}
class User {
String name;
int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
|
四、Map
接口
Map
中的key
:无序的、不可重复的,使用Set
存储所有的key
Map
的value
:无序的、可重复的,使用Collection
存储所有的value
一个键值对:key-value
构成了一个Entry
对象;Map
中的Entry
是无序的、不可重复的,使用Set
存储所以的Entry
1. HashMap
底层实现原理
jdk7
的实现:
-
HashMap hashMap = new HashMap();
在实例化以后,底层创建了长度是16的一维数组Entry[] table
-
map.put(key1, value2)
-
首先调用key1
所在类的hashCode()
计算key1
的哈希值,此哈希值经过某种算法以后,得到在Entry
数组中的存放位置。
- 如果此位置上的数据为空,此时的
key1-value1
添加成功。--> 情况1
- 如果此位置上的位置不为空,一位着此位置上存在一个或多个数据(以链表形式存在),此时比较
key1
和已经存在的一个或多个数据的哈希值
- 如果
key1
的哈希值与已经存在的数据的哈希值都不同,此时key1-value1
添加成功。 --> 情况2
- 如果
key1
的哈希值与已经存在的某一个数据的哈希值相同,继续调用key
所在类的equals()
方法比较:
- 如果
equals()
返回false
,此时key1-value1
添加成功 --> 情况3
- 如果
equals()
返回true
,使用value1
替换相同key
的value
值
关于情况2和情况3,此时key1-value1
和原来的数据以链表的方式存储。
-
在不断的添加过程中,会设计到扩容问题,默认的扩容方式:扩容为原来容量的2倍,并将原有的数据复制过来。
jdk8
的实现
HashMap hashMap = new HashMap();
底层没有长度为16的Entry
数组,底层的数组是Node[]
,而非Entry[]
- 首次调用
put()
方法时,底层创建长度为16的数组
jdk7
的底层结构只有数据+链表。jdk8
中底层结构为数据+链表+红黑树。当数组的某一个索引位置上的元素一链表形式存在的数据个数>8且当前数组的长度>64时,此时此索引位置上的所有数据改为红黑树存储
2. Map
的常用方法
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 | import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class HashMapTest {
public static void main(String[] args) {
HashMap map1 = new HashMap();
// 添加 put()
map1.put("name", "Tom");
map1.put("age", 18);
HashMap map2 = new HashMap();
map2.put("gender", "man");
// 添加 putAll()
map1.putAll(map2);
System.out.println(map1); // {gender=man, name=Tom, age=18}
// 删除, 被删除的元素存在返回该元素的值,不存在返回null
Object value = map1.remove("name");
System.out.println(value); // Tom
System.out.println(map1); // {gender=man, age=18}
// 修改
map1.put("age", 16);
System.out.println(map1); // {gender=man, age=16}
// clear()
// map1.clear();
// System.out.println(map1); // {}
// get() 获取指定key对应的value,存在则返回value,不存在返回null
System.out.println(map1.get("gender")); // man
// containsKey() 是否包含指定的key
System.out.println(map1.containsKey("age")); // true
// containsValue() 是否包含指定的value
System.out.println(map1.containsValue("man")); // true
// size() 返回map中key-value的个数
System.out.println(map1.size()); // 2
// isEmpty() 判断当前map是否为空
System.out.println(map1.isEmpty()); // false
// equals(obj) 判断当前map与参数对象obj是否相等
System.out.println(map1.equals(map2)); // false
// keySet() 遍历所有的key
Set set = map1.keySet();
Iterator iterator = set.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
// values() 遍历所有的values
Collection values = map1.values();
for (Object obj : values) {
System.out.println(obj);
}
// entrySet() 遍历所有的key-value
Set entrySet = map1.entrySet();
Iterator iterator1 = entrySet.iterator();
while (iterator1.hasNext()) {
Object obj = iterator1.next();
Map.Entry entry = (Map.Entry) obj;
System.out.println(entry.getKey() + "-->" + entry.getValue());
}
}
}
|
3. TreeMap
向TreeMap
中添加key-value
,要求key必须是由同一个类创建的对象,因为要按照key进行排序:自然排序、定时排序
4. Properties
在项目下创建一个文件jdbc.properties
使用
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 | import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;
public class PropertiesTest {
public static void main(String[] args) {
FileInputStream stream = null;
try {
Properties properties = new Properties();
stream = new FileInputStream("jdbc.properties");
properties.load(stream);
String user = properties.getProperty("user");
String passwd = properties.getProperty("passwd");
System.out.println(user);
System.out.println(passwd);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
|
五、Collections
常用方法
操作Collection
和Map
的工具类
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 | import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class CollectionsTest {
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add(8);
list.add(3);
list.add(9);
list.add(5);
System.out.println(list); // [8, 3, 9, 5]
// reverse(list) 反转
Collections.reverse(list);
System.out.println(list); // [5, 9, 3, 8]
// shuffle(list) 随机排序
Collections.shuffle(list);
System.out.println(list); // [9, 8, 5, 3]
// sort() 排序
Collections.sort(list);
System.out.println(list); // [3, 5, 8, 9]
System.out.println(Collections.max(list));
System.out.println(Collections.min(list));
// copy()
List dest = Arrays.asList(new Object[list.size()]);
Collections.copy(dest, list);
System.out.println(dest); // [3, 5, 8, 9]
}
}
|