Java集合-Map
Map(java.util.Map)接口,代表着key和value间的映射,更具体地说,Java映射可以存储键和值对,一旦存储在地图中,以后就可以只使用键来查找值。Map接口不是Collection 的子接口,因此,它的行为与其他Collection 类型稍有不同。
Map的实现
既然Map是个接口,因此初始化时需要使用她的具体实现,Map包括以下实现类:
java.util.HashMap
java.util.Hashtable
java.util.EnumMap
java.util.IdentityHashMap
java.util.LinkedHashMap
java.util.Properties
java.util.TreeMap
java.util.WeakHashMap
根据我的经验来最常用的是HashMap和TreeMap。在迭代映射时,每一个Map实现的行为都与元素的顺序以及在映射中插入和访问元素所需的时间(big O表示法)稍有不同。
HashMap映射键值,但是不保证存储在map的内部顺序。
TreeMap 同样映射键值,但是可以保证key或者value的顺序。
如不需要元素的顺序的话可以使用HashMap,因为HashMap速度很快,否则的话使用TreeMap。
创建Map
创建新的Map必须使用它的实现之一:
Map mapA = new HashMap(); Map mapB = new TreeMap();
Map的泛型
默认可以往Map里面添加任何对象,但是用了泛型后,可以限制其类型:
Map<String, MyObject> map = new HashMap<String, MyObject>();
这个Mao只能接受String类型的Key和MyObject 实例的value,同时访问和迭代它们不需要强制类型转换:
for(MyObject anObject : map.values()){ //do someting with anObject... } for(String key : map.keySet()){ MyObject value = map.get(key); //do something to value }
如果指定要往Map中插入的对象,则定义时考虑使用泛型,这样避免了往Map中添加错误的对象。
Map中插入元素
可以通过put()方法添加元素:
Map<String, String> map = new HashMap<>(); map.put("key1", "element 1"); map.put("key2", "element 2"); map.put("key3", "element 3");
调用了三次put()方法添加键值,可以通过键key获取对应的值value。
只能插入对象
Map的键值只能插入对象,如果将原始值(例如int、double等)作为键或值传递给映射,则在作为参数传递之前,原始值将自动装箱,下面是自动装箱的例子:
map.put("key", 123);
上面的例子中,value的值添加了原始值int,java对自动装箱变成Integer实例,因为put()方法需要的是对象实例。
后续插入相同的键key
一个key在Map中只能出现一次,这意味着Map只能存在同一对键值对,也就是同一个Map中只能存在一个“key1“的key值。如果多次调用put()方法插入相同的值,那么之前的值会被覆盖。
Null值的Key
可以用null作为key放到Map中:
Map map = new HashMap(); map.put(null, "value for null key");
可以通过get()方法获取key为null的值:
Map<String, String> map = new HashMap<>(); String value = map.get(null);
Null值得Values
Value得值同样允许为null:
map.put("D", null);
请记住,稍后使用该键调用get()时将得到一个null值-因此这将返回null:
Object value = map.get("D");
代码执行后value变量的值将是null。
把另外一个Map中的所有元素插入Map
Map接口中有一个putAll()方法,可以将另外一个Map实例中的所有键值拷贝到当前Map中,实际上就是两个Map的合集,下面是代码:
Map<String, String> mapA = new HashMap<>(); mapA.put("key1", "value1"); mapA.put("key2", "value2"); Map<String, String> mapB = new HashMap<>(); mapB.putAll(mapA);
执行完代码后,mapB 将包含mapA中所有的键值对,条目的复制只有一种方式。调用 mapB.putAll(mapA) 只会将mapA中的元素添加到mapB中,而不会将mapB中的元素添加到mapA中。
从Map中获取元素
可以通过Map的get()方法获取指定key值的元素值:
Map map = new HashMap(); map.put("key1", "value 1"); String element1 = (String) map.get("key1");
注意get()方法返回的是 Object,所以需要强制转换成String (因为知道是String)。下面是使用泛型的用法:
Map<String, String> map = new HashMap<>(); map.put("key1", "value 1"); String element1 = map.get("key1");
获取默认值
Map接口有个 getOrDefault()方法,这个方法如果Map中没有对应key的值则返回一个默认值:
Map<String, String> map = new HashMap<>(); map.put("A", "1"); map.put("B", "2"); map.put("C", "3"); String value = map.getOrDefault("E", "default value");
这个例子Map创建,并且存储了key值为 A, B 和 C。然后调用了Map的getOrDefault()方法,参数为String类型的 E的key值,因此会返回默认值,因为Map中不包含E这个key, 默认值是作为第二个参数传入的。
检查Map是否包含某个Key
可以使用Map的 containsKey()方法,检查是否包含某个key:
boolean hasKey = map.containsKey("123");
代码执行后,hasKey变量的值是true,我们map中包含key”123”,否则返回false。
检查是否包含某个Value
Map接口中同样有个containsValue() 方法可以检查是否包含某个value:
boolean hasValue = map.containsValue("value 1");
执行完代码后,如果map中有value为“value 1“的值则hasValue返回true,否则返回false。
根据Keys迭代Map
有下面几种方法可以迭代Map的key:
- 通过key 的Iterator
- 通过for-each循环
- 通过Stream
通过Key的Iterator
通过Map 的 keySet()迭代所有的key:
Iterator iterator = map.keySet().iterator(); while(iterator.hasNext(){ Object key = iterator.next(); Object value = map.get(key); }
上面例子,key 的Iterator返回Map中的每个key,可以调用next()方法,一个个的返回。获取到了key就可以通过get()方法获取到对应的值。下面看看使用泛型:
Map<String, String> map = new HashMap<>(); Iterator<String> iterator = map.keySet().iterator(); while(iterator.hasNext(){ String key = iterator.next(); String value = map.get(key); }
注意泛型同样使用了map.keySet().iterator()方法。
通过Key 的For-Each循环
Java 5以后可以使用for-each循环迭代 key :
for(Object key : map.keySet()) { Object value = map.get(key); }
上面代码的效果与前一节中显示的代码非常相似,如果使用了泛型,可以在for-each循环内使用类型,不需要强制转换:
Map<String, String> map = new HashMap<>(); for(String key : map.keySet()) { String value = map.get(key); }
通过Key的Stream
Java 8以后可以使用Java Stream迭代Map的keys, Stream 接口是 Java Stream API 已不是,首先需要从key的Sey中获取Stream:
Map<String, String> map = new HashMap<>(); map.put("one", "first"); map.put("two", "second"); map.put("three", "third"); Stream<String> stream = map.keySet().stream(); stream.forEach((value) -> { System.out.println(value); });
迭代Map的Values
同样可以迭代Map的Values,首先通过values()获取 Collection,然后迭代Collection,有下面几种方法:
- 通过Iterator
- 通过for-each 循环
- 通过Stream
通过Value的Iterator
首先获取values的Set中Iterator 实例:
Map<String, String> map = new HashMap<>(); Iterator<String> iterator = map.values().iterator(); while(iterator.hasNext()) { String nextValue iterator.next(); }
由于一个集合是无序的,所以不能保证值集中的值的迭代顺序,但是如果是TreeSet则可以控制顺序。
使用Value 的For-Each循环
第二种方法是通过for-each循环:
Map<String, String> map = new HashMap<>(); for(String value : map.values()){ System.out.println(value); }
通过Value的 Stream
第三种方法是使用Java Stream API,首先通过Map获取value的Set,然后再获取 Stream:
Map<String, String> map = new HashMap<>(); map.put("one", "first"); map.put("two", "second"); map.put("three", "third"); Stream<String> stream = map.values().stream(); stream.forEach((value) -> { System.out.println(value); });
迭代Map的Entries
同样可以迭代Map中的所有entries,通过entries意味着键值对key + value ,一个entry 同时包含了key 和value ,之前我们只是迭代了单个key或者value,现在我们同时迭代key和value,有下面两种方式:
- 通过Entry 的Iterator
- 通过for-each循环
通过Entry的 Iterator
第一种方式是通过从Map中的entry的Set中获取entry的 Iterator :
Set<Map.Entry<String, String>> entries = map.entrySet(); Iterator<Map.Entry<String, String>> iterator = entries.iterator(); while(iterator.hasNext()) { Map.Entry<String, String> entry = iterator.next(); String key = entry.getKey(); String value = entry.getValue(); }
注意这么从Map.Entry 中获取key和 value。
通过Entry的 For-Each循环
第二种方法就是使用for-each循环:
for(Map.Entry<String, String> entry : map.entrySet()){ String key = entry.getKey(); String value = entry.getValue(); }
从Map中移除Entries
可以使用(Object key)方法移除Entries,因此,可以移除与键匹配的(key,value)对:
map.remove("key1");
执行完代码后,map中不再包含key1的键值对。
移除所有Entries
可以通过Map的clear()方法移除所有元素:
map.clear();
替换Map中的Entry
可以通过Map 的 replace()方法替换其中的元素,如果Map中不存在此key的值,则不会做任何事,则有key存在才会替换,和put()方法的工作原理不一样:
Map map = new HashMap<>(); map.replace("key", "val2"); //no "key" entry, no replace map.put("key", "val1"); //now contains "key" entry map.replace("key", "val2"); //now "key" entry replaced
执行完代码后key对应的值是val2。
获取Map中Entries的大小
可以通过 Map 的size() 方法获取Map的 entries数量:
int entryCount = map.size();
检查Map是否是空
Map接口中有一个专门的方法isEmpty() 检查Map是否是空,如果是空返回true否则返回false。