Set底层是Map,区别在于Set将每个Key对应的Value,都统一为
private static final Object PRESENT = new Object();
JDK8 的Map特点:
1.Map和Collection属于同一级别,前者为双列集合,后者为单列集合,Map是用于保存具有映射关系的数据:Key-Value。
2. Map中的key和 value 可以是任何引用类型的数据,会封装到 HashMap$Node 对象中
3. Map 中的 key 不允许重复,原因和 HashSet 一样,当加入相同的key相当于进行替换
4. Map 中的 value 可以重复
5. Map 的 key 可以为 null, value 也可以为 null ,注意 key 为 null,只能有一个,value 为 null ,可以多个
6. 经常用 String 类作为 Map 的 key7. key 和 value 之间存在单向一对一关系,即通过指定的 key 总能找到对应的 value8.与hashset一样不保证映射顺序,因为底层是哈希表存储9.hashmap没有实现同步,线程不安全,方法没有做同步互斥,没有synchronized关键字
Map存放数据的key-value:一对key-value就是放在一个HashMap$Node里面,又因为Node实现了Entry接口,有些书上也说一对k-v就是一对Entry//添加元素时,调用putVal函数 public V put(K key, V value) { return putVal(hash(key), key, value, false, true); } //将元素构建成Node类型结点其放入tab[i] = newNode(hash, key, value, null); //也就是说最终存放的K-V实际上是:HashMap$Node类型的结点: Node<K,V> newNode(int hash, K key, V value, Node<K,V> next) { return new Node<>(hash, key, value, next); } //为了遍历方便,还会创建一个EntrySet集合,该集合存放的元素类型为Entry,而一个Entry对象就包含了一个K-V,即创建了一个EntrySet<Entry<K, V>> transient Set<Map.Entry<K,V>> entrySet; //输出entrySet的运行时类型 Set set = map.entrySet(); System.out.println(set.getClass()); // 输出为HashMap$EntrySet //在entrySet中,定义的元素类型为Map.Entry,但是存放的数据类型为HashMap$Node //运行以下代码,印证 for (Object obj: set){ System.out.println(obj.getClass);//输出为HashMap$Node }//能这样做的原因就是HashMap$Node 和 Map.Entry有如下关系: //static class Node<K,V> implements Map.Entry<K,V> 接口多态 // 当把Node放入到EntrySet中时就方便遍历了 //因为底层的EntrySet提供了两个重要的方法: K getKey(); V getValue(); //因此当我们在遍历Map的时候就可以: for (Object obj: set){ //先向下转型 Map.Entry entry = (Map.Entry)obj; System.out.println(entry.getKey() + "-" + entry.getValue()); }
//当我们传入的k,v是一个对象的时候,debug更能直观的看到entryset和node的关系 public static void main(String[] args) { HashMap map = new HashMap(); map.put(new A("kk"), new B(15)); map.put(new A("jj"), new B(51)); }
debug信息如下:table表地址和entryset中的table指向同一个地址,table表中的Node结点地址和entryset 的table表中结点指向同一地址
总结 :
Map存储元素的底层仍然是存储的哈希表table+链表(红黑树),但是为了方便遍历,在底层又实现了一个EntrySet,将存储的Node结点k-v组成一个个Entry,将这些entry存放到entryset中,同时对于该entryset,提供了两个方法K getKey();V getValue();,来获取对应的k - v,并且为了能够更严苛的遍历条件,比如只想得到key的集合,那么entryset提供了一个Set类keySet(Set的实现子类)用来遍历所有的key,还提供了一个Collection类型的集合Values(Collection的实现子类)来遍历value
final class KeySet extends AbstractSet<K> final class Values extends AbstractCollection<V>
//通过如下代码印证: Set set1 = map.keyset(); System.out.println(set1.getClass());//Java.util.HashMap$KeySet Collection values = map.values(); System.out.println(values.getClass());//java.util.HashMap$Values
Map接口常用方法:
public static void main(String[] args) { HashMap map = new HashMap(); //put map.put("邓超", new A("KK"));//OK map.put("邓超", "孙俪"); map.put("aa", "bb"); map.put("mm", "bb"); //remove, 根据键删除映射关系 map.remove("邓超"); System.out.println("map = " + map); //get 根据键获取值 Object val = map.get("aa"); System.out.println("val = " + val); //size 获取元素个数 System.out.println("k-v = " + map.size()); //isEmpty:判断个数是否为0 System.out.println(map.isEmpty());//F //clear,清空键值对 //map.clear(); //containsKey:查找键是否存在,containsValue:查找值是否存在 System.out.println("结果 = " + map.containsKey("aa")); System.out.println("结果 = " + map.containsValue("bb")); }
遍历Map:
//遍历方式 //1.获取键集合,遍历 Set set = map.keySet(); //增强for for (Object obj : set){ System.out.println(obj + "-" + map.get(obj)); } //迭代器 Iterator iterator = set.iterator(); while(iterator.hasNext()){ Object key = iterator.next(); System.out.println(key + "+" + map.get(key)); } //错误的遍历方式 // while(iterator.hasNext()){ // System.out.println(iterator.next() + "+" + map.get(iterator.next())); // } //2.通过entryset Set set1 = map.entrySet(); for(Object o : set1){ //向下转型 Map.Entry entry = (Map.Entry)o; System.out.println(entry.getKey() + "-" + entry.getValue()); } //3.通过entryset的迭代器 Iterator iterator1 = map.entrySet().iterator(); while(iterator1.hasNext()){ Map.Entry entry = (Map.Entry)iterator1.next(); System.out.println(entry.getKey() + "-" + entry.getValue()); }