Java SE 高级开发之集合类三之——Map从使用到底层剖析

前面我们介绍了Collection集合接口,Collection集合的特点是每次进行单个对象的保存,如果现在要进行一对对象(偶对象)的保存就只能使用Map集合来完成,即Map集合中会一次性保存两个对象,且这两个对象的关系:key=value结构。这种结构最大的特点是可以通过key找到对应的value内容。

Map接口概述

首先来观察Map接口定义:

public interface Map<K,V>

在Map接口中有如下常用方法:
这里写图片描述

Map本身是一个接口,要使用Map需要通过子类进行对象实例化。Map接口的常用子类有如下四个: HashMap、Hashtable、TreeMap、ConcurrentHashMap。

HashMap子类(常用)

HashMap是使用Map集合中最为常用的子类。

例:Map基本操作

public class Test {
    public static void main(String[] args) {
        Map<Integer, String> map = new HashMap<>();
        map.put(1, "hello");
        //观察插入重复的key值时
        map.put(1, "java");
        map.put(3, "world");
        System.out.println(map);
        //根据key值取得value
        System.out.println(map.get(3));
        //观察找一个不存在的key值
        System.out.println(map.get(10));
    }
}

这里写图片描述

例:取得Map中所有key信息
这里写图片描述

剖析HashMap原理
在数据量小的(JDK1.8后阈值为8)时候HashMap是按照链表的模式存储的;当数据量变大之后为了进行快速查找,会将这个链表变为红黑树(均衡二叉树)来进行数据保存,用hash码作为数据定位。

HashMap的特点:

1.底层实现的是链表数组,JDK1.8后又加了红黑树。
2.实现了Map全部的方法。
3.key用Set存放,key值不能重复,key对应的类需要重写hashCode和equals方法。
4.允许空键和空值(但空键只有一个,且放在第一位,value的值可以任意重复或者任意为空)。
5.元素是无序的,而且顺序会不定时改变。
6.插入、获取的时间复杂度基本是O(1)。
7、遍历整个Map需要的时间与桶(数组)的长度成正比(因此初始化时Hash Map的容量不宜太大)。
8.Hash Map为异步处理,非线程安全。

剖析源码

这里请参照大神解析:

HashMap主要特点和关键方法源码解读
https://blog.csdn.net/u011240877/article/details/53351188

HashMap在JDK1.8后新增的红黑树结构
https://blog.csdn.net/u011240877/article/details/53358305

Hashtable子类

JDK1.0提供有三大主要类:Vector、Enumeration、Hashtable。Hashtable是最早实现这种二院偶对象数据结构,后期的设计也让其与Vector一样多实现了Map接口而已。

例:观察Hashtable

public class Test{
    public static void main(String[] args) {
        Map<Integer, String> map = new Hashtable<>();
        map.put(1, "hello");
        //观察不能插入重复的key
        map.put(1, "java");
        map.put(2, "hello");
        map.put(3, "world");
        System.out.println(map);
    }
}

这里写图片描述

HashMap与Hashtable的区别
这里写图片描述

ConcurrentHashMap子类

ConcurrentHashMap的特点 = Hashtable的线程安全性 + HashMap的高性能,在使用ConcurrentHashMap处理的时候,既可以保证多个线程更新数据的同步,又可以保证很高效的查询速度。ConcurrentHashMap不允许key为null。

首先来看ConcurrentHashMap的定义

public class ConcurrentHashMap<K,V> extends AbstractMap<K,V> implements ConcurrentMap<K,V>,Serializable

例:使用ConcurrentHashMap

public class Test{
    public static void main(String[] args) {
        Map<Integer, String> map = new ConcurrentHashMap<>();
        map.put(1, "hello");
        //观察不能插入重复的key
        map.put(1, "java");
        map.put(2, "hello");
        map.put(3, "world");
        System.out.println(map);
    }
}

这里写图片描述

使用起来很简单,因为ConcurrentHashMap也是Map接口的子类。下面我们来分析一下ConcurrentHashMap的工作原理。
如果采用一定的算法,将保存的大量数据平均分在不同的桶(数据区域),这样在进行数据查找的时候就可以避免全局的数据扫描。

例:数据分桶

public class Test{
    public static void main(String[] args) {
        for(int i = 0; i < 10; i++) {
            Random random = new Random();
            int temp = random.nextInt(5555);
            int result = temp % 3;
            switch(result) {
            case 0:
                System.out.println("第0桶: " + temp);
                break;
            case 1:
                System.out.println("第1桶: " + temp);
                break;
            case 2:
                System.out.println("第2桶: " + temp);
                break;
            }
        }
    }
}

这里写图片描述
采用了分桶之后每一个数据中必须有一个明确的分桶标记,我们一般采用hashCode()。

这里写图片描述
数据更新的时候只锁更新的对应区域,其他区域的访问不受影响。

这里写图片描述

Map集合使用Iterator输出

Map接口与Collection接口不同,Collection接口有iterator()方法可以很方便的取得Iterator对象来输出,而Map接口本身并没有此方法。下面我们首先来观察Collection接口与Map接口数据保存的区别:
这里写图片描述
在Map接口里面有一个重要的方法,将Map集合转为Set集合:

public Set<Map.Entry<K, V>> entrySet();

Map要想调用Iterator接口输出,走的是一个间接使用的模式,如下图:
这里写图片描述
例:通过Iterator输出Map集合

public class Test{
    public static void main(String[] args) {
        Map<Integer, String> map = new HashMap<>();
        map.put(1, "hello");
        map.put(2, "java");
        map.put(3, "hello");
        map.put(4, "world");
        //将Map集合转为Set集合
        Set<Map.Entry<Integer, String>> set = map.entrySet();
        //获取Iterator对象
        Iterator<Map.Entry<Integer, String>> iterator = set.iterator();
        //输出
        while(iterator.hasNext()) {

            System.out.println(iterator.next());
        }
    }
}

这里写图片描述

关于Map中key的说明
在之前使用Map集合的时候使用的都是系统类作为key(Integer,String等)。实际上用户也可采用自定义类作为key。
这个时候一定要记得覆写Object类的hashCode()与equals()方法。
例:观察自定义类作为Key,系统类作为Value的情况(覆写)

class Person{
    private Integer age;
    private String name;

    public Person(Integer age, String name) {
        this.age = age;
        this.name = name;
    }

    public String toString() {
        return "Person{ " + "age = " + age + 
                "name = " + name + " }";
    }

    public boolean equals(Object o) {
        if(this == o) return true;
        if(o == null || this.getClass() != o.getClass())
            return false;
        Person person = (Person)o;
        return Objects.equals(this.name, person.name)
                && Objects.equals(this.age, person.age);
    }
    public int hashCode() {
        return Objects.hash(age, name);
    }
    public Integer getAge() {
        return this.age;
    }
    public String getName() {
        return this.name;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public void setName(String name) {
        this.name = name;
    }
}

public class Test{
    public static void main(String[] args) {
        Map<Person, String> map = new HashMap<>();
        map.put(new Person(15, "zhangsan"), null);
        System.out.println(map);
    }
}

这里写图片描述
实际开发来讲,我们一般都是采用系统类(String,Integer等)作为Key值,这些系统类都帮助用户覆写好了hashCode()与equals()方法。

TreeMap子类

TreeMap是一个可以排序的Map子类,它是按照Key的内容排序的。
例:观察TreeMap的使用

public class Test{
    public static void main(String[] args) {
        Map<Integer, String> map = new TreeMap<>();
        map.put(2, "c");
        map.put(0, "a");
        map.put(1,"b");
        System.out.println(map);
    }
}

这里写图片描述
这个时候的排序处理依然按照的是Comparable接口完成的。
结论:有Comparable出现的地方,判断数据就依靠compareTo()方法完成,不再需要equals()与hashCode()

Map集合小结:

1.Collection保存数据的目的一般用于输出(Iterator),Map保存数据的目的是为了根据key查找,找不到返回null。
2.Map使用Iterator输出(Map.Entry的作用)。
3.HashMap数据结构一定要理解(链表与红黑树)、HashMap与Hashtable区别。

猜你喜欢

转载自blog.csdn.net/guijun6/article/details/80522171
今日推荐