HashMap基本用法
上参考链接:https://blog.csdn.net/weixin_43263961/article/details/86427533
Part1:put, remove, containsKey, containsValue;
HashMap<String, String> map = new HashMap<>(); map.put("1", "a"); map.put("2", "b"); map.put("3", "a"); map.put("4", "c"); //map.put("4", "d"); //可以运行键值都为"4"(如果键相同,后者覆盖前者) System.out.println(map); System.out.print(map.keySet()+" "); //集合中所有键以Set集合形式返回 System.out.println(); System.out.print(map.values()+" "); //集合中所有值以Collection集合形式返回 System.out.println(); System.out.println("集合大小:"+map.size()); System.out.println("是否包含该键:"+map.containsKey("2")); //返回boolean System.out.println("是否包含该值:"+map.containsValue("b")); //返回boolean System.out.println(map.isEmpty()); //判断是否为空,若不包含键-值映射关系则返回true map.remove("4"); //删除映射关系 System.out.println(map); map.clear();//清空集合 System.out.println(map);
public V put(K key, V value) 方法源代码解析
public V put(K key, V value) { // 如果 key 为 null,调用 putForNullKey 方法进行处理 if (key == null) return putForNullKey(value); // 根据 key 的 keyCode 计算 Hash 值 int hash = hash(key.hashCode()); // 搜索指定 hash 值在对应 table 中的索引 int i = indexFor(hash, table.length); // 如果 i 索引处的 Entry 不为 null,通过循环不断遍历 e 元素的下一个元素 for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; // 找到指定 key 与需要放入的 key 相等(hash 值相同 // 通过 equals 比较放回 true) if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } // 如果 i 索引处的 Entry 为 null,表明此处还没有 Entry modCount++; // 将 key、value 添加到 i 索引处 addEntry(hash, key, value, i); return null; }
关于代码中的 entry.recordAccess(this)函数:
这是考虑到LinkedHashMap(链表形式的HashMap)的特性:
Linked内部含有一个private transient Entry header;来记录元素插入的顺序或者是元素被访问的顺序。利用这个线性结构的对象,可以帮助记录entry加入的前后顺序或者记录entry被访问的频率(最少被访问的entry靠前,最近访问的entry靠后)。大致的过程如下:
new LinkedHashMap(10, 0.75, true);
其中前面两个参数就是HashMap构造函数需要的参数,后面的true表明LinkedHashMap按照访问的次序来排序。
按照访问的次序来排序的含义:当调用LinkedHashMap的get(key)或者put(key, value)时,碰巧key在map中被包含,那么LinkedHashMap会将key对象的entry放在线性结构的最后。
按照插入顺序来排序的含义:调用get(key), 或者put(key, value)并不会对线性结构产生任何的影响。
void recordAccess(HashMap m) { LinkedHashMap lm = (LinkedHashMap)m; if (lm.accessOrder) { lm.modCount++; remove(); addBefore(lm.header); //将该entry放在header线性表的最后 } }
正是因为LinkedHashMap提供按照访问的次序来排序的功能,所以它才需要改写HashMap的get(key)方法(HashMap不需要排序)和HashMap.Entry的recordAccess(HashMap)方法。总之:如果有对于Map中元素位置依据插入/查找顺序排列的需求,覆写HashMap中的recordAccess方法!
至于初始化!!江湖传说还有这样一种初始化方法,比上面的简洁一丢丢:
HashMap<String, String> map = new HashMap<String, String>() { { map.put("name", "test"); map.put("age", "20"); } };
第一层括弧实际是定义了一个匿名内部类 (Anonymous Inner Class),第二层括弧实际上是一个实例初始化块 (instance initializer block),这个块在内部匿名类构造时被执行。推而广之,可初始化ArrayList、Set等其他集合类。然而当我把HashMap定义成类的成员变量,在定义处使用给这个初始化块,会报NullPointer错,后面再把这个问题深入研究一下。
Part2:存取值
get (取key对应的Value)
map.put("DEMO", 1); /*Value的类型*///得到map中key相对应的value的值 System.out.println(map.get("DEMO"));//1 System.out.println(map.get("1"));//null 需要自己实现、取Value对应的Key的方法
取Value对应的Key
第一种方法:map.keySet()
(1) For-Each
public static Object getKey(HashMap map, Object value){ ArrayList<Object> keyList = new ArrayList<>(); for(Object key: map.keySet()){ if(map.get(key).equals(value)){ keyList.add(key); } } return keyList; }
(2) 迭代器
public static Object getKey(HashMap map, Object value){ ArrayList<Object> keyList = new ArrayList<>(); Set <Object> keys = map.keySet(); Iterator<Object> it = keys.iterator(); Object key; while (it.hasNext()) { key = it.next(); if(map.get(key).equals(value)){ keyList.add(key); } } return keyList; }
第二种方法:map.entrySet()
(1) For-Each
public static Object getKey(HashMap map, Object value){ ArrayList<Object> keyList = new ArrayList<>(); Set<Entry<Object, Object>> entrySet = map.entrySet(); for (Entry<Object,Object> entry:entrySet) { if(entry.getValue().equals(value)){ keyList.add(entry.getKey()); } } return keyList; }
(2) 迭代器
public static Object getKey(HashMap map, Object value){ ArrayList<Object> keyList = new ArrayList<>(); Set<Entry<Object, Object>> entrySet = map.entrySet(); Iterator<Entry<Object, Object>> it = entrySet.iterator(); Entry<Object, Object> entry; while(it.hasNext()){ entry = it.next(); if(entry.getValue().equals(value)){ keyList.add(entry.getKey()); } } return keyList; }
Part3:遍历
第一种方法:使用keySet()
这种方法遍历了2次,一次是转为iterator,另一次是从HashMap中取出key所对应的value。
(1) For-Each
Set<String> keys = map.keyset(); for(String key: keys){ System,out.println(key+":"+map.get(key)); }
(2) 迭代器
Set<String> keys = may.keyset(); Iterator<String> IT= keys.iterator(); String key; while(it.hasNext()){ key = it.next(); System,out.pritln(key+":"+map.get(key)); }
第二种方法:使用entrySet()
首先先来说一下Entry,由于Map中存放的元素均为键值对,故每一个键值对必然存在一个映射关系。
Map中采用Entry内部类来表示一个映射项,映射项包含Key和Value (我们总说键值对键值对, 每一个键值对也就是一个Entry)
Map.Entry里面包含getKey()和getValue()方法。
大部分时候,对于Map的遍历我们都是借助于Entry来做的。
Iterator<Map.Entry<Integer, Integer>> it=map.entrySet().iterator(); //建立一个entry对象的的迭代器 while(it.hasNext()) { Map.Entry<Integer,Integer> entry=it.next(); int key=entry.getKey(); int value=entry.getValue(); System.out.println(key+" "+value); }
这种方法只遍历了1次,它把key和value都放到了entry中,因此比keySet()快。
(1) For-Each(普遍选它)
Set<Entry<String,String>> entrySet = map.entrySet(); //map.entrySet()返回<key,value>键值对的集合 for (Entry<String,String> entry:entrySet) { System.out.println(entry.getKey()+":"+entry.getValue()); //entry.getKey()返回key,entry.getValue()返回value }
(2) 迭代器
Set<Entry<String,String>> entrySet = map.entrySet(); Iterator<Entry<String,String>> it = entrySet.iterator(); Entry<String,String> entry; while (it.hasNext()) { entry = it.next(); System.out.println(entry.getKey()+":"+entry.getValue()); }
总结:当map中容量较大时,推荐使用第二种entrySet()的方法。