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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
|
package cn.xpshuai.java1;
import org.junit.Test;
import java.util.*;
/**
* @author: 剑胆琴心
* @create: 2021-01-27 16:22
* @功能:
*
*
*一、Map的结构体系
* | ----- Map接口: 双列数据,保存具有映射关系的"key-value"对的集合(类似高中函数: y = f(x),不同key可以指向相同的value)
* |---- HashMap(面试问的多, 平时也常用): 作为Map的主要实现类; 线程不安全的,效率高;存储null的key和value
* |---- LinkedHashMap:保证在遍历map元素时,可以按照添加的循序实现遍历
* 原因:在原有的HashMap的基础上,添加了一堆指针,指向前一个和后一个元素
* 对于频繁的遍历操作,此类执行效率高于HashMap
* |---- TreeMap: 保证按照添加的key-value进行排序,实现排序遍历(此时考虑key的自然排序和定制排序)
* |---- Hashtable: 作为古老的实现类;线程安全的,效率低;不能存储null的key和value
* |---- Properties: 常用来处理配置文件。key和value都是String类型
*
*
* HashMap的底层:数组+链表(jdk7及之前)
* 数组+链表+红黑树(jdk8)
*
*
*二、Map结构的理解
* Map中的key: 无序的、不可重复的,使用Set存储所有的key ---> key所在的类要重写equaLs( )和hashcode() (以HashMap为例)
* Map中的value:无序的、可重复的,使用Collection存储所有的value -->vaLue所在的类要重写equals()
* 一个键值对:key-value构成了一个Entry对象
* Map中的Entry: 无序的、不可重复的,使用Set存储所有的entry
*
*
*三、HashMap的底层实现原理
* jdk7为例:
* HashMap map = new HashMap();
* 在实例化以后,底层创建了长度为16的以为数组Entry[] table
* ... 可能已经执行了多次put ...
* map.push(key1, value1);
* 首先,计算key1所在类的hashCode(),计算key1的哈希值,此哈希值经过某种算法计算以后,得到在Entry数组中的存放位置
* 如果此位置上的数据为空,此时的key1-value1添加成功 ---> 情况1
* 如果此位置上的数据不为空(此位置存在一个或多个数据(以链表形式存在)), 比较key1和已经存在的一个或多个数据的hash值:
* 如果key1的hash值与已存在的数据的hash值都不相同,此时key1-value1添加成功 ---> 情况1
* 如果key1的hash值与已存在的数据的某一个数据(key2-value2)的hash值都不相同,继续比较: 调用key1所在类的equals()方法,比较:
* 如果equals()返回false:此时key1-value1添加成功 ---> 情况3
* 如果equals()返回true:使用value1替换相同key的value2值,
* key1-value1添加成功
* 补充:关于情况2和情况3: 此时key1-value1和原来的数据以链表的方式存储
*
* 在不断的添加过程中,会涉及到扩容问题, 当超出临界值(且要存放的位置非空)时,扩容。默认的扩容方式为扩容到原来容量的2倍,并将原有数据复制过来
*
* jdk8 相较于jdk7,在底层实现方面的不同;
* 1.new HashMap(): 底层没有创建一个长度为16的数组
* 2.jdk8底层的数据是 Node[], 而非Entry[]
* 3.首次调用put()方法时,底层创建长度为16的数组
* 4.jdk7底层结构只有:数组+链表, jdk8中底层结构:数组+链表+红黑树
* 当数组的某一个索引位置上的元素以链表形式存在的数据个数>8 且当前数组的长度>64时,
* 此时此索引位置上所有数据改为红黑树存储
*
*
*HashMap源码分析:
* jdk7
* jdk8
*
*
* DEFAULT_INITIAL_CAPACITY : HashMap的默认容量,16
* DEFAULT_LOAD_FACTOR: HashMap的默认加载因子:0.75
* threshold:扩容的临界值,=容量*填充因子:16* 0.75 =>12
* TREEIFY_THRESHOLD: Bucket中链表长度大于该默认值,转化为红黑树:8
* MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量:64
*
*
*
* 面试题:
* 1.HashMap的底层实现原理(重点)
* 2.HashMap和Hashtable区别
* 3.CurrentHashMap与Hashtable区别
*
*
*
* 四、LinkedHashMap的底层实现:
* static class Entry<K,V> extends HashMap.Node<K,V> {
* Entry<K,V> before, after; //能够记录添加的元素的先后顺序
* Entry(int hash, K key, V value, Node<K,V> next) {
* super(hash, key, value, next);
* }
* }
*
*
*
* 五、Map中常用的方法
*
*
* 六、TreeMap
* //向TreeMap中添加key-value,要求key必须是由同一个类创建的对象
* //因为要按照key进行排序:自然排序、定制排序
*
*
*
*/
public class MapTest {
@Test
public void test1(){
Map map = new HashMap();
// map = new HashMap();
map.put(null, 123);
}
@Test
public void test2(){
HashMap map = new HashMap();
// map = new HashMap();
map.put(null, 123);
}
@Test
public void test3(){
HashMap map = new HashMap();
map = new LinkedHashMap(); // 记录了添加的顺序
map.put(123, "AA");
map.put(124, "BB");
map.put(567, "CC");
System.out.println(map);
// LinkedHashMap
}
/*
Map中常用的方法
*/
@Test
public void tes4(){
// Object put(Object key, Object value):将制定键值对添加(修改)到当前map对象中
// void putAll(Map m): 将m中所有键值对存放到当前map中
// Object remove(Object key): 移除指定key的键值对,并返回value
// void clear(): 清空当前map中的所有数据
HashMap map = new HashMap();
//put 添加
map.put(123, "AA");
map.put("BB", "222");
map.put(567, "CC");
map.put(56, "CCC");
// put修改
map.put(56, "566666");
//putAll()
HashMap map2 = new HashMap();
map2.put(1, "AA");
map2.put(2, "AB");
map.putAll(map2);
//remove
Object val = map.remove("BB");
System.out.println(val);
System.out.println(map);
//clear
// map.clear();
System.out.println(map.size());
//元素查询的操作:
//object get(0bject key):获取指定key对应的vaLue
System.out.println(map.get(45));
//boolean containsKey (object key):是否包含指定的key
System.out.println(map.containsKey(45));
//boolean containsValue(object value):是否包含指定的value
// int size():返回map 中key-value对的个数
//boolean isEmpty():判断当前map是否为空
System.out.println(map.isEmpty());
//boolean equals(object obj):判断当前map和参数对象obj是否相等
System.out.println(map.equals(map2));
// 元视图操作的方法(遍历):
// set keySet():返回所有key构成的Set集合
// 遍历所有key
Set set = map.keySet();
Iterator iterator = set.iterator(); //变成set了,就可以用迭代器了
while (iterator.hasNext()){
System.out.println(iterator.next());
}
// collection values():返回所有value构成的collection集合set entryset():返回所有key-value对构成的Set集合
Collection values = map.values();
for(Object obj: values){ // 变成collections了,就可以用foreach了
System.out.println(obj);
}
//遍历所有的key-value:
// 方式1:entrySet()
Set entrySet = map.entrySet();
Iterator iterator1 = entrySet.iterator();
while (iterator1.hasNext()){
Object obj = iterator1.next();
// entrySet集合中的元素都是entry
Map.Entry entry = (Map.Entry)obj;
System.out.println(entry.getKey() + " --- " + entry.getValue());
}
//方式2:
Set set2 = map.keySet();
Iterator iterator2 = set2.iterator(); //变成set了,就可以用迭代器了
while (iterator2.hasNext()){
Object key = iterator2.next();
System.out.println(key+ "<--->" + map.get(key));
}
}
@Test
public void test5(){
//向TreeMap中添加key-value,要求key必须是由同一个类创建的对象
// 因为要按照key进行排序:自然排序、定制排序
// //TreeMap自然排序
TreeMap map = new TreeMap();
Person p1 = new Person("TOm", 18);
Person p2 = new Person("Jerry", 18);
Person p3 = new Person("Jack", 20);
Person p4 = new Person("Rose", 21);
Person p5 = new Person("Lura", 22);
map.put(p1, 98);
map.put(p2, 99);
map.put(p3, 100);
map.put(p4, 88);
map.put(p5, 92);
Set entrySet = map.entrySet();
Iterator iterator1 = entrySet.iterator();
while (iterator1.hasNext()){
Object obj = iterator1.next();
// entrySet集合中的元素都是entry
Map.Entry entry = (Map.Entry)obj;
System.out.println(entry.getKey() + " --- " + entry.getValue());
}
// //TreeMap定制排序
TreeMap map2 = new TreeMap(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
// 比较年龄
if (o1 instanceof Person && o2 instanceof Person){
Person p1 = (Person)o1;
Person p2 = (Person)o2;
return Integer.compare(p1.getAge(), p2.getAge());
}
throw new RuntimeException("输入的类型不匹配");
}
});
Person p11 = new Person("TOm", 18);
Person p21 = new Person("Jerry", 18);
Person p31 = new Person("Jack", 20);
Person p41 = new Person("Rose", 21);
Person p51 = new Person("Lura", 22);
map2.put(p11, 98);
map2.put(p21, 99);
map2.put(p31, 100);
map2.put(p41, 88);
map2.put(p51, 92);
Set entrySet2 = map2.entrySet();
Iterator iterator2 = entrySet2.iterator();
while (iterator2.hasNext()){
Object obj = iterator2.next();
// entrySet集合中的元素都是entry
Map.Entry entry2 = (Map.Entry)obj;
System.out.println(entry2.getKey() + " --- " + entry2.getValue());
}
}
}
|