Map接口的实现类

HashMap

HashMap源码阅读

LinkedHashMap

LinkedHashMap是HashMap的子类,实际上它连HashMap的putVal等方法都没有重写,因为HashMap就调用了预留给子类的函数,在HashMap中是空实现,在LinkedHashMap中重写,用作建立双向链表

static class Entry<K,V> extends HashMap.Node<K,V> {
    Entry<K,V> before, after;
    Entry(int hash, K key, V value, Node<K,V> next) {
        super(hash, key, value, next);
    }
}

这是LinkedHashMap中的一个类,结合上方信息可看出,LinkedHashMap并没有修改HashMap的数据结构,只是在HashMap的基础上添加了两个元素

newNode方法是putVal中调用的,生成节点的方法,在linkedHashMap也被重写了

Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
    LinkedHashMap.Entry<K,V> p =
        new LinkedHashMap.Entry<K,V>(hash, key, value, e);
    linkNodeLast(p);
    return p;
}

可以看到,一旦有新元素,那么它就会被放在链表的结尾,那么,如果有修改元素的情况出现呢

在putVal中有这样一段
其中afterNodeAccess在HashMap是空实现,在LinkedHashMap中被重写

if (e != null) { // existing mapping for key
    V oldValue = e.value;
    if (!onlyIfAbsent || oldValue == null)
    e.value = value;
    afterNodeAccess(e);
    return oldValue;
}

实际上,afterNodeAccess这个方法的就是,将被修改的类置于链表末尾

void afterNodeAccess(HashMap.Node<K,V> e) { // move node to last
    LinkedHashMap.Entry<K,V> last;
    if (accessOrder && (last = tail) != e) {
        LinkedHashMap.Entry<K,V> p =
                (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
        p.after = null;
        if (b == null)
            head = a;
        else
            b.after = a;
        if (a != null)
            a.before = b;
        else
            last = b;
        if (last == null)
            head = p;
        else {
            p.before = last;
            last.after = p;
        }
        tail = p;
        ++modCount;
    }
}

Hashtable

Hashtable确实老了,从jdk1.0开始,Hashtable就已经出现了.它甚至都没有按照驼峰命名法来命名

一般说来,大家都知道Hashtable和HashMap的区别就在于,Hashtable线程安全,而HashMap线程不安全,但是差距肯定不止于此
由于Iterate和Map出现得比较晚,所以Hashtable没有使用这两个东西,看他独特的继承树,就知道它是版本弃子.
如果jdk开发人员没有放弃这个类,完全可以完全重写它.
即使Hashtable线程安全,但是在使用多线程时,还是建议使用Map的同步代理类,说明Hashtable可以完全被遗弃了.
在功能方面,Hashtable不允许空值

if (value == null) {
     throw new NullPointerException();
}

这是Hashtable的put中第一句

在这里有杜绝了key为null的可能

int hash = key.hashCode();

那么HashMap是怎么处理的呢?
首先HashMap没有特别判断value为空的情况
而hashMap的key的hash值是这么计算的:

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

我看了一下,Hashtable既没有扰乱hash值的操作,也没有红黑树,也没有位运算优化...

还有一些方法功能上的差别,不细说了

TreeMap

要使用这个类,不需要重写hashcode方法与equals方法,但是Key一定要实现Compareble接口或者在创建TreeMap时,传入一个Compartor的实现类
TreeMap会优先使用Comparator
TreeMap内部直接是一个红黑树,所以,内部的Key是有序的,要想查找Key的最大最小值都比较容易
但是,如何进行遍历呢?
当然想都不用想,用递归实现树的前序遍历可以做到,但是,用户调用Iterate的next方法的时候,树里面的值可不是全部直接出来了
显然这里不能使用递归

首先要知道,在调用Iterate的构造器时,指针就已经指向了数中的最小值

//这个方法用于确定t的下一个元素
static <K,V> TreeMap.Entry<K,V> successor(Entry<K,V> t) {

    if (t == null)
        return null;
    右侧不为空,往右侧走一步,接着找到右子树的最小值
    else if (t.right != null) {
        Entry<K,V> p = t.right;
        while (p.left != null)
            p = p.left;
        return p;
    } else {
    //parent这个属性就是去掉递归的核心
        Entry<K,V> p = t.parent;
        Entry<K,V> ch = t;
        while (p != null && ch == p.right) {
            ch = p;
            p = p.parent;
        }
        return p;
    }
}

当然相关方法和HashMap也有区别,能写出红黑树的话就差不多能看懂了

ConcurrentHashMap


这个类是线程同步的,不支持null
实际上它的做法就是在table数组上每一个散列位置上加锁,而不是像Hashtable那样吧整张表给锁了
显而易见,如果要用到多线程,一定要深入了解这个类
但是目前我对sun.misc.Unsafe的了解还不够,而这个类中用到了它,所以无法对源码进行跟深入的分析

以后再回头单开一个博客介绍一下这个类吧,同学们一定要搞清楚这个类呀

posted @ 2020-03-20 14:39  断腿三郎  阅读(216)  评论(0编辑  收藏  举报