Java Map基础知识必知必会

Map用于保存具有映射关系的数据,因此Map集合里保存着两组值,一组值用于保存Map里的key,另一组值用于保存Map里的value,key和value都可以是任何引用类型的数据。Map的key不允许重复,即同一个Map对象的任何两个key通过equals方法比较总是返回false,如果后面插入的key与前边的重复了,则后面的会将前边的覆盖掉。
在这里插入图片描述
key和value总是存在单向一对一的关系,即通过指定的key,总是能找到唯一的、确定的value。也就是说从Map中取数据时,只需要给出指定的key,就可以取出对应的value。
Map与Set的关系十分密切,如果把value看做是key的附庸品,key在哪value就在哪,在存储key的时候也是随机不重复的,这样就像极了Set。事实上,Map提供了一个方法keySet()来返回该Map中所有key组成的Set集合。从Java源码来看,Java是先实现了Map,然后通过包装一个所有的value都为null的Map就实现了Set集合。
如果只看Map里的value,不得不说它们又像极了一个List:元素之间可以重复,每个元素都通过特定的索引值(key)来查找。

一、HashMap

HashMap为Map的典型实现类,它允许key和value为null(key只能有一个null),它不保证其中键值对的顺序,它是线程不安全的,底层是实现为数组+链表的形式。类似于HashSet,判断key相等的标准也是:两个key通过equals()方法比较返回true,两个值的hashCode也相等。判断value相等的标准是:两个对象通过equals()方法返回true即可。
HashMap初始大小为16,增长因子为0.75,一次扩容后大小为原来的2倍(满足2的倍数)。
遍历HashMap的方法一般有四种:通过keySet(),forEach和iterator。以及通过entrySet(),同样的forEach和iterator。
遍历HashMap的四种方式(其他也适用)

public class MainTest {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        map.put("hty", 1111);
        map.put("qaq", 666);
        map.put("qwq", 123);
        System.out.println("通过keySet()的forEach遍历");
        Set<String> key = map.keySet();
        for(String s : key){
            System.out.println(s + " -- " + map.get(s));
        }
        System.out.println("通过keySet()的iterator遍历");
        Iterator iterator = map.keySet().iterator();
        while(iterator.hasNext()){
            Object key1 = iterator.next();
            System.out.println(key1 + " -- " + map.get(key1));
        }
        System.out.println("通过entrySet()的forEach遍历");
        for(Map.Entry<String, Integer> m: map.entrySet()){
            System.out.println(m.getKey() + " -- " + m.getValue());
        }
        System.out.println("通过entrySet()的iterator遍历");
        Iterator<Map.Entry<String, Integer>> iterator1 = map.entrySet().iterator();
        while(iterator1.hasNext()){
            Map.Entry<String, Integer> m = iterator1.next();
            System.out.println(m.getKey() + " -- " + m.getValue());
        }
    }
}
输出结果:
通过keySet()的forEach遍历
qaq -- 666
qwq -- 123
hty -- 1111
通过keySet()的iterator遍历
qaq -- 666
qwq -- 123
hty -- 1111
通过entrySet()的forEach遍历
qaq -- 666
qwq -- 123
hty -- 1111
通过entrySet()的iterator遍历
qaq -- 666
qwq -- 123
hty -- 1111
可以看出与插入顺序并不一致。

二、Hashtable

从名字就可以看出来,Hashtable是一个古老的实现类,它底层是数组+链表,而不像HashMap的数组+链表+红黑树。它不允许null作为key和value,如果试图把null放入Hashtable中,将会引发NullPointerException异常,它是线程安全的。

三、LinkedHashMap

作为HashMap的子类,LinkedHashMap使用双向链表来维护key-value的次序,其实就是负责维护Map的迭代顺序使与插入顺序保持一致。它维护了元素的插入顺序,因此性能略低于HashMap,但是迭代访问却不错。

四、TreeMap

TreeMap就是一个红黑树数据结构,每个key-value对即作为红黑树的一个节点。TreeMap存储key-value对时,需要根据key对节点进行排序。TreeMap可以保证所有的key-value对处于有序状态,TreeMap有两种排序方式:自然排序(所有的key必须事先Comparable接口,而且所有的key应该是同一种对象,否则将会抛出ClassCastException异常),定制排序(创建TreeMap时,传入一个Comparator对象,该对象负责对TreeMap中的所有key进行排序。定制排序不要求Map的key实现Comparable接口)。TreeMap中的key不允许null值,如果插入给key赋null会抛出NullPointerException异常,value允许null值。

五、WeakHashMap

WeakHashMap与HashMap的区别在于,HashMap的key保留了对实际对象的强引用,这意味着只要HashMap对象不被销毁,该HashMap的所有key所引用的对象就不会被垃圾回收,HashMap也不会自动删除这些key所对应的key-value对;但WeakHashMap的key只保留了对实际对象的弱引用,这意味着如果WeakHashMap对象的key所引用的对象没有被其他强引用变量所引用,则这些key所引用的对象可能被垃圾回收,WeakHashMap也可能自动删除这些key所对应的key-value对。

public class MainTest {
    public static void main(String[] args) {
        Map map = new WeakHashMap();
        map.put(2134,412);
        map.put(1111, 1111);
        map.put(1112,1111);
        map.put("qaq", 1111);
        System.out.println(map);
        System.gc();
        System.out.println(map);
    }
}
输出结果:
{1111=1111, qaq=1111, 1112=1111, 2134=412}
{qaq=1111}

如果要使用WeakHashMap的key来保留对象的弱引用,则不要让该key所引用的对象具有任何强引用,否则将会失去使用WeakHashMap的意义。

六、IdentityHashMap

IdentityHashMap在处理两个key是否相等时比较独特:当且仅当两个key严格相等(key1 == key2)时,它才认为两个key相等;而对于普通的HashMap而言,只要key1和key2通过equals()方法比较返回true,且他们的hashCode值相等即可。

猜你喜欢

转载自blog.csdn.net/laobanhuanghe/article/details/101157007
今日推荐