Java JDK7源码-java.util.Map<K,V>

源码

1
package java.util;
2
3
public interface Map<K,V> {
4
    // 查询操作
5
6
    /**
7
     * 返回map中键值对的个数
8
     * 如果该个数大于Integer.MAX_VALUE,则返回Integer.MAX_VALUE
9
     */
10
    int size();
11
12
    /**
13
     * 若map不包含任何键值对则返回true
14
     */
15
    boolean isEmpty();
16
17
    /**
18
     * 若map包含key则返回true
19
     * 更确切的说,当且仅当map包含k,有key==null ? k==null : key.equals(k)时,返回true
20
     * 
21
     * @throws ClassCastException key的类型与map不合(可选)
22
     * @throws NullPointerException key==null且map禁止包含null(可选)
23
     */
24
    boolean containsKey(Object key);
25
26
    /**
27
     * 若map中存在1个或多个key的值为入参则返回true
28
     * 更确切的说,当且仅当map包含至少一个值v,有value==null ? v==null : value.equals(v)时,返回true
29
     * 通常来说,在多数实现中,本操作的时间复杂度为线性阶(依map的大小)
30
     * 
31
     * @throws ClassCastException value的类型与map不合(可选)
32
     * @throws NullPointerException value==null且map禁止包含null(可选)
33
     */
34
    boolean containsValue(Object value);
35
36
    /**
37
     * 返回map中key所对应的值
38
     * 若map中未包含key,则返回null
39
     *
40
     * 更确切的说,若map包含键值对k-v,并有
41
     * key==null ? k==null : key.equals(k)
42
     * 则返回v
43
     * 反之返回null
44
     * 因为map中键不允许重复,则至多只会有一个对应的值,因此不会产生歧义
45
     *
46
     * 基于map中的键值是否允许为null,共有如下四种情况:
47
     * 
48
     * 1. 键,值均不允许为null
49
     * 返回null: map中不包含key
50
     * 返回非null: map中包含key
51
     * 
52
     * 2. 键允许为null,值不允许为null
53
     * 返回null: map中不包含key
54
     * 返回非null: map中包含key
55
     * 
56
     * 3. 键不允许为null,值允许为null
57
     * 返回null: 无法判断。有可能map有不包含key,也有可能map中包含key,不过其值为null
58
     * 返回非null: map中包含key
59
     * 
60
     * 4. 键值均允许为null
61
     * 返回null: 无法判断。有可能map有不包含key,也有可能map中包含key,不过其值为null
62
     * 返回非null: map中包含key
63
     * 
64
     * 由此分析,是否能根据本方法的返回值判断map中是否包含key,与键是否允许为null无关,只与值是否允许为null有关
65
     * 在无法判断时,可调用containsKey(Object key)辨认
66
     *
67
     * @throws ClassCastException key的类型与map不合(可选)
68
     * @throws NullPointerException key==null且map禁止包含null(可选)
69
     */
70
    V get(Object key);
71
72
    // 改变操作
73
74
    /**
75
     * 本方法属于破坏性方法,可选
76
     * 将key-value作为键值对存入map
77
     * 若map此前包含key,即当且仅当containsKey(key)返回true,则其原值将被新的value替换,并返回原值
78
     * 若map此前未包含key,则返回null
79
     * 需要注意的是,返回null并不能说明map此前未包含key,也有可能map此前包含key,只不过该key的原值就为null(当前,前提是实现类允许值为null)
80
     *
81
     * @throws UnsupportedOperationException map不支持本方法
82
     * @throws ClassCastException key或value因其类型禁止被插入map
83
     * @throws NullPointerException key为null且map的键不允许为null 或 value为null且map的值不允许为null
84
     * @throws IllegalArgumentException key或value因其某些属性禁止被插入map
85
     */
86
    V put(K key, V value);
87
88
    /**
89
     * 本方法属于破坏性方法,可选
90
     * 若map中包含key,则将其从map中移除
91
     * 更确切的说,若map包含键值对k-v,并有
92
     * key==null ?  k==null : key.equals(k)
93
     * 则移除该键值对
94
     * 因为map中键不允许重复,则至多只会移除一个键值对,因此不会产生歧义
95
     * 
96
     * 若有键值对被移除,则本方法返回对应的值。反之返回null
97
     * 需要注意的是,返回null并不能说明没有键值对被移除,也有可能map此前包含key,只不过该key的值就为null(当前,前提是实现类允许值为null)
98
     *
99
     * 一旦本方法被调用,map将不再包含key
100
     *
101
     * @throws UnsupportedOperationException map不支持本方法
102
     * @throws ClassCastException key的类型与map不合(可选)
103
     * @throws NullPointerException key为null且map的键不允许为null(可选)
104
     */
105
    V remove(Object key);
106
107
108
    // 批量操作
109
110
    /**
111
     * 本方法属于破坏性方法,可选
112
     * 将m中所有的键值对复制入map
113
     * 本方法等效于遍历m,随后将m中所有的键值对以put(K key, V value)依次插入map中
114
     * 本接口并未约束如下情况时的解决策略:在键值对存入map的过程中m发生变化
115
     * 
116
     * @throws UnsupportedOperationException map不支持本方法
117
     * @throws ClassCastException m中的某个key或value因其类型禁止被插入map
118
     * @throws NullPointerException m==null 或m中存在为null的key且map的键不允许为null 或 m中存在为null的value且map的值不允许为null
119
     * @throws IllegalArgumentException m中的某个key或value因其某些属性禁止被插入map
120
     */
121
    void putAll(Map<? extends K, ? extends V> m);
122
123
    /**
124
     * 本方法属于破坏性方法,可选
125
     * 移除map中所有的键值对
126
     * 本方法调用后map将为空
127
     * 
128
     * @throws UnsupportedOperationException map不支持本方法
129
     */
130
    void clear();
131
132
133
    // 视图
134
135
    /**
136
     * 返回一个视图,该视图的类型为Set,该set由map中所有的key组成
137
     * 既然该set是视图,那么作用于map之上的改变就会反映在该set上,反之亦然
138
     * 
139
     * 若set正在迭代的过程中,map因非set的原因发生了结构性变化
140
     * (也就是不是由set的Iterator导致的变化,或者更具体的说,不是由set的Iterator的remove()方法导致的变化)
141
     * 则迭代结果将被置为未定义
142
     * 
143
     * 通过set的Iterator的Iterator.remove
144
     * 或set本身的Set.remove,removeAll,retainAll,clear
145
     * 操作,可以将key自set中移除,同时,map中的以对应key为键的键值对也会被移除
146
     * 
147
     * 该set视图不支持添加操作,即不支持add,addAll
148
     * (当然啦,只添加一个key是无法形成键值对的)
149
     * 强行调用会抛出UnsupportedOperationException
150
     * 
151
     * 注1:keySet()测试
152
     */
153
    Set<K> keySet();
154
155
    /**
156
     * 返回一个视图,该视图的类型为Collection,该collection由map中所有的value组成
157
     * 既然该collection是视图,那么作用于map之上的改变就会反映在该collection上,反之亦然
158
     * 
159
     * 若collection正在迭代的过程中,map因非collection的原因发生了结构性变化
160
     * (也就是不是由collection的Iterator导致的变化,或者更具体的说,不是由collection的Iterator的remove()方法导致的变化)
161
     * 则迭代结果将被置为未定义
162
     * 
163
     * 通过collection的Iterator的Iterator.remove
164
     * 或collection本身的Collection.remove,removeAll,retainAll,clear
165
     * 操作,可以将value自collection中移除,同时,map中的以对应value为值的键值对也会被移除
166
     * 
167
     * 该collection视图不支持添加操作,即不支持add,addAll
168
     * (当然啦,只添加一个value是无法形成键值对的)
169
     * 强行调用会抛出UnsupportedOperationException
170
     * 
171
     * 注2:values()测试
172
     */
173
    Collection<V> values();
174
175
    /**
176
     * 返回一个视图,该视图的类型为Set,该set由map中所有的键值对组成
177
     * 既然该set是视图,那么作用于map之上的改变就会反映在该set上,反之亦然
178
     * 
179
     * 若set正在迭代的过程中,map因非set的原因发生了结构性变化
180
     * 则迭代结果将被置为未定义
181
     * 
182
     * 通过set的Iterator的Iterator.remove
183
     * 或set本身的Set.remove,removeAll,retainAll,clear
184
     * 操作,可以将键值对自set中移除,同时,map中对应的键值对也会被移除
185
     * 
186
     * 该set视图不支持添加操作,即不支持add,addAll
187
     * 强行调用会抛出UnsupportedOperationException
188
     * 
189
     * 注3:entrySet()测试
190
     */
191
    Set<Map.Entry<K, V>> entrySet();
192
193
    /**
194
     * 一个Entry对象实际上就是map中的一个键值对
195
     * 
196
     * Map.entrySet方法会返回一个类型为Set的视图,它的元素就是Entry类型
197
     * 如果想要获得map中的Entry,唯一的方式就是调用Map.entrySet方法,然后使用iterator迭代该set
198
     * 迭代出的Map.Entry对象仅在迭代期间有效
199
     * 更正式的说,除了因为该set本身导致map发生的变化,在通过iterator迭代该set得到Map.Entry对象期间,如果map发生变化,迭代出的Map.Entry对象将被置为未定义
200
     */
201
    interface Entry<K,V> {
202
        /**
203
         * 返回本entry中存储的key
204
         *
205
         * @throws IllegalStateException 如果entry已被自map中移除,那么实现类可以(但不是必须)抛出本异常
206
         */
207
        K getKey();
208
209
        /**
210
         * 返回本entry中存储的value
211
         * 若entry已被自map中移除(通过iterator的remove操作),结果将被置为未定义
212
         * 
213
         * @throws IllegalStateException 如果entry已被自map中移除,那么实现类可以(但不是必须)抛出本异常
214
         */
215
        V getValue();
216
217
        /**
218
         * 本方法可选
219
         * 将entry中存储的值替换为value,而后返回旧值
220
         * (相应的,map中对应的键值对中的值也会发生变化)
221
         * 
222
         * 未定义如下情况时本方法的行为:entry已被自map中移除(通过iterator的remove操作)
223
         * 
224
         * @throws UnsupportedOperationException map不支持put操作
225
         * @throws ClassCastException value因其类型禁止被插入map
226
         * @throws NullPointerException value==null且map的值不允许为null
227
         * @throws IllegalArgumentException value因其某些属性禁止被插入map
228
         * @throws IllegalStateException 如果entry已被自map中移除,那么实现类可以(但不是必须)抛出本异常
229
         */
230
        V setValue(V value);
231
232
        /**
233
         * 比较o与entry的相等性
234
         * 若o同样是Map Entry且o与entry代表的键值对相等,则返回true
235
         * 更正式的说,如果满足如下条件,则可认为两个entry e1 e2代表的键值对相等:
236
         * 
237
         * if (
238
         * e1.getKey()==null ? e2.getKey()==null : e1.getKey().equals(e2.getKey())
239
         * ) && (
240
         * e1.getValue()==null ? e2.getValue()==null : e1.getValue().equals(e2.getValue())
241
         * )
242
         * 
243
         * 以这种方式设计的话,即便e1 e2的实现类不同,也可以正确的判断二者是否相等
244
         */
245
        boolean equals(Object o);
246
247
        /**
248
         * 返回entry的hash code值
249
         * 定义方式如下:
250
         * (
251
         * e.getKey()==null ? 0 : e.getKey().hashCode()
252
         * ) ^ (
253
         * e.getValue()==null ? 0 : e.getValue().hashCode()
254
         * )
255
         * 
256
         * 这样的定义方式确保了对于任意Entry而言,只要有
257
         * e1.equals(e2)
258
         * 即有
259
         * e1.hashCode()==e2.hashCode()
260
         * 遵循equals方法与hashCode方法的设计规范
261
         */
262
        int hashCode();
263
    }
264
265
    // 比较与哈希
266
267
    /**
268
     * 比较o与map的相等性
269
     * 若o同样是一个Map且o与map中存储的键值对均相等,则返回true
270
     * 更正式的说,如果满足如下条件,则可认为两个Map m1 m2相等:
271
     * m1.entrySet().equals(m2.entrySet())
272
     * 以这种方式设计的话,即便m1 m2的实现类不同,也可以正确的判断二者是否相等
273
     */
274
    boolean equals(Object o);
275
276
    /**
277
     * 返回map的hash code值
278
     * 定义方式为:
279
     * map.entrySet()返回的set中的Entry的hash code之和
280
     * 
281
     * 这样的定义方式确保了对于任意Map而言,只要有
282
     * m1.equals(m2)
283
     * 即有
284
     * m1.hashCode()==m2.hashCode()
285
     * 遵循equals方法与hashCode方法的设计规范
286
     */
287
    int hashCode();
288
289
}

已整理层级关系

直接实现本接口的类

综述

本接口是Java集合框架中的一员。其中K,V分别是map中key与value的类型。

本接口约束了一个由复数个键值对(key-value)构成的map对象。map不能包含重复的key,每个key至多映射至一个value。

本接口(诞生于JDK1.2)取代了抽象类java.util.Dictionary<K,V>(诞生于JDK1.0)的地位。

本接口提供了3种用于查看map中的内容的集合视图(collection view):

  1. 由map中的key构成的set
  2. 由map中的value构成的collection
  3. 由map中的键值对(key-value)构成的set

所谓map中元素的顺序,其实就是在使用这3种方式查看map时,对应的collection的iterator返回元素的顺序。某些本接口的实现类,例如TreeMap,会明确保证map中的元素有序;而对于另一些实现而言,例如HashMap,则不会保证这一点。

需要注意的事,如果作为map key的对象时常会变化,并且这种变化在一定程度上会影响到相等性的判断,那么操作该map后得到的结果就无法完全被控制。基于这个原因,map禁止以其自身作为自己的key。同时,虽然并未禁止,但也应极其谨慎对待的是map以其自身作为自己的value:因为此时map的equals及hashCode方法将难以编写。

通常来说,所有的本接口的实现类都需要提供两个”标准的”构造函数(之所以打上引号,是因为接口是无法约束实现类的构造函数的编写规范的,更遑论标准,这只是一个约定俗成的建议,起码JDK中本接口的实现类都是遵循的):

  1. 创建一个空的map的无参构造函数
  2. 接收一个Map类型参数的构造函数,它会以入参为基础,创建一个类型为自身实现类的,由相同键值对(key-value)构成的新map

事实上,我们可以使用第二个构造函数完成map的复制:只要入参和待生成的新map从属于相同的实现类型即可。

本接口包含所谓的”破坏性方法”,这些方法会改变它们所操作的map。如果某实现类不打算实现某个破坏性方法,那么对于这个方法,该实现类应抛出UnsupportedOperationException。该约束其实是比较灵活的:即便某实现类不支持某破坏性方法,但如果该次调用不可能产生实际上的破坏性影响,那么是否抛出UnsupportedOperationException就不加限制了,完全由实现类的编写者决定。例如有不可变的map对象,它显然是不支持本接口所约束的putAll(Map<? extends K, ? extends V> m),不过如果作为入参的m为空,那么本次调用将无论如何也不会修改该不可变map对象,此时不抛出异常也是可以的。

某些本接口的实现类可能会对它所包含的key或value有所限制。例如,某些实现类禁止包含值为null的key或value,某些对其key的类型有所限制。试图插入一个不合规的key或value会抛出unchecked exception(对于那些并非强烈需要实现类抛出的异常,本接口会将其标注为可选),典型的诸如NullPointerException或ClassCastException。试图查询一个不合规的key或value可能会导致抛出异常,或者仅仅只是返回false。

本接口的很多方法都是基于Object类的equals(Object obj)定义的。例如containsKey(Object key)的文档中就有这样的叙述:

1
当且仅当map包含k,有key==null ? k==null : key.equals(k)时,返回true

该叙述并不意味着如果我们以非null的key去调用containsKey(Object key),就一定需要使用key.equals(k)来比较。只要实现了与equals等效的比较结果,实现类可以基于自身特点编写更优化的解决方案。仍以本方法为例,我们可以举出一个比较通用的优化方法,实现类可以先用hashCode()比较key与k的hashCode是否相同(当然了,这需要key与k所属的类遵循hashCode()与equals()的规范),这样就可以快速过滤掉hashCode不相等的情况。

注1:keySet()测试

1
import java.util.HashMap;
2
import java.util.Map;
3
import java.util.Set;
4
5
public class Test {
6
7
    public static void main(String[] args) {
8
        Map<String, String> map = new HashMap<String, String>();
9
        map.put("a", "A");
10
        map.put("b", "B");
11
        Set<String> keySet1 = map.keySet();
12
        Set<String> keySet2 = map.keySet();
13
        System.out.println(keySet1 == keySet2);
14
    }
15
}

输出:

1
true

说明多次调用keySet返回的是唯一的视图对象。


1
import java.util.HashMap;
2
import java.util.Map;
3
import java.util.Set;
4
5
public class Test {
6
7
    public static void main(String[] args) {
8
        Map<String, String> map = new HashMap<String, String>();
9
        map.put("a", "A");
10
        map.put("b", "B");
11
        Set<String> keySet = map.keySet();
12
        for (String key : keySet) System.out.print(key + " ");
13
        System.out.println();
14
        map.put("c", "C");
15
        for (String key : keySet) System.out.print(key + " ");
16
    }
17
}

输出:

1
b a 
2
b c a

说明作用于map上key的修改可以影响keySet。反之亦然,在此就不写测试代码了。


1
import java.util.HashMap;
2
import java.util.Map;
3
import java.util.Set;
4
5
public class Test {
6
7
    public static void main(String[] args) {
8
        Map<String, String> map = new HashMap<String, String>();
9
        map.put("a", "A");
10
        map.put("b", "B");
11
        Set<String> keySet = map.keySet();
12
        keySet.add("c");
13
        for (String key : keySet) System.out.print(key + " ");
14
    }
15
}

输出:

1
Exception in thread "main" java.lang.UnsupportedOperationException
2
	at java.util.AbstractCollection.add(AbstractCollection.java:260)
3
	at com.ansjseg.Test.main(Test.java:14)

即强行调用keySet的add会抛出UnsupportedOperationException。

注2:values()测试

1
import java.util.Collection;
2
import java.util.HashMap;
3
import java.util.Map;
4
import java.util.Map.Entry;
5
6
public class Test {
7
8
    public static void main(String[] args) {
9
        Map<String, String> map = new HashMap<String, String>();
10
        map.put("a", "A");
11
        map.put("b", "B");
12
        map.put("c", "B");
13
        Collection<String> values = map.values();
14
        values.remove("B");
15
        for (Entry<String, String> entry : map.entrySet()) System.out.println(entry.getKey() + " - " + entry.getValue());
16
    }
17
}

输出:

1
c - B
2
a - A

因为map中key是唯一的,因此如果我们移除keySet()返回的视图中的key,那么可以确切的知道哪个键值对遭到了删除。

然而map中的value却是可重复的,因此如果我们如果只移除values()返回的视图中的某个value,在有复数个键值对的value等于被删除值时,被删除的键值对是不可预测的。


1
import java.util.Collection;
2
import java.util.HashMap;
3
import java.util.Map;
4
5
public class Test {
6
7
    public static void main(String[] args) {
8
        Map<String, String> map = new HashMap<String, String>();
9
        map.put("a", "A");
10
        map.put("b", "B");
11
        Collection<String> values = map.values();
12
        values.add("C");
13
    }
14
}

输出:

1
Exception in thread "main" java.lang.UnsupportedOperationException
2
	at java.util.AbstractCollection.add(AbstractCollection.java:260)
3
	at com.ansjseg.Test.main(Test.java:14)

即强行调用values的add会抛出UnsupportedOperationException。

注3:entrySet()测试

1
import java.util.HashMap;
2
import java.util.Map;
3
import java.util.Set;
4
5
public class Test {
6
7
    public static void main(String[] args) {
8
        Map<String, String> map1 = new HashMap<String, String>();
9
        map1.put("a", "A");
10
        map1.put("b", "B");
11
        Map<String, String> map2 = new HashMap<String, String>();
12
        map2.put("c", "C");
13
        Set<Map.Entry<String, String>> entrySet1 = map1.entrySet();
14
        Set<Map.Entry<String, String>> entrySet2 = map2.entrySet();
15
        entrySet1.addAll(entrySet2);
16
    }
17
}

输出:

1
Exception in thread "main" java.lang.UnsupportedOperationException
2
	at java.util.AbstractCollection.add(AbstractCollection.java:260)
3
	at java.util.AbstractCollection.addAll(AbstractCollection.java:342)
4
	at com.ansjseg.Test.main(Test.java:17)

即强行调用entrySet的addAll会抛出UnsupportedOperationException。