HashMap与Hashtable、currenthashmap的区别

Hashtable:

1)继承dictionary类,底层哈希表,线程安全的。key和value都不允许为null。

 

HashMap:

实现:HashMap<K,V> extends AbstractMap<K,V>实现Cloneable, Serializable两个接口。

线程不安全。允许key和value为null。

构造函数:HashMap():初始容量16,加载因子为0.75。

     HashMap(int initialCapctity):初始容量自定义,加载因子0.75。

     HashMap(int initialCapctity, float loadFactor):初始容量和加载因子都可以自定义。

加载因子:加载因子的系数小于等于1,意指  即当 元素个数 超过 容量长度*加载因子的系数 时,进行扩容。默认为0.75。即表示列表中元素的填满程度,是冲突机会与空间利用率的一种平衡。

数据结构数组实现,每个位置用链表实现,即数组+链表,当链表长度超过8时转化成红黑树。

 

 

 hashmap中的一些面试题:

 

“你用过hashmap吗”“什么是hashmap?你为什么用到它”

当然这几个问题可以回答一些特性即可

“你知道hashmap的工作原理吗”“你知道hashmap的get()方法吗”

这才是我们的重点。"HashMap基于hashing原理,使用put(key,value)存储对象到hashmap中,使用get(key,value)从hashmap中获取对象。当我们给put()方法传递键和值时,先调用hashcode()方法,返回的hashcode用于找到bucket位置存储Entry对象。"       重点: hashmap是在bucket中存储键对象和值对象,作为map.Entry。

 

当两个对象的hashcode相同会发生什么”   

hashcode相同说明他们的bucket相同,“碰撞会发生”。因为hashmap使用链表存储对象,这个entry(包含有键值对的map.entry对象)会存在链表中。如何准确获取两个键的hashcode相同的对象?

答:找到bucket后,调用keys.equals()的方法去找到链表中正确的节点。(equals方法与==的区别,前文博客中有讲到,比较两个对象的存储地址是否相等)。

“hashmap的扩容原理”

初始容量量是16,负载因子是0.75,当一个map填满了75%的bucket的时候,和其他集合类一样,将会创建原来hashmap大小两倍的bucket数组,来重新调整map的大小,并将原来的对象放入新的bucket数组中。这个过程叫rehashing ,因为它调用hash方法找到新的bucket位置。

 

还有一些其他问题

1、为什么String,Integer适合作主键?

因为String是不可变的,也是final的,而且已经重写了equals()和hashcode的方法了。计算hashcode的时候就要防止键值的改变,如果键值在放入时和获取时返回不同的hashcode的话,就不能从hashmap中获取想要的对象。线程安全。

2、我们可以使用自定义的对象作为键吗?

可以使用任何对象作为键,只要准守了equals()和hashcode()的方法,并且插入到map中不会不会再改变了。

3、可以使用concurrentHashMap代替hashtable吗

当然可以,concurrentHashMap就是hsahtable的增强版,可以替代,甚至性能更好。

 

public HashMap(int initialCapacity, float loadFactor) {
        //初始容量不能<0
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: "
                    + initialCapacity);
        //初始容量不能 > 最大容量值,HashMap的最大容量值为2^30
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        //负载因子不能 < 0
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: "
                    + loadFactor);

        // 计算出大于 initialCapacity 的最小的 2 的 n 次方值。
        int capacity = 1;
        while (capacity < initialCapacity)
            capacity <<= 1;
        
        this.loadFactor = loadFactor;
        //设置HashMap的容量极限,当HashMap的容量达到该极限时就会进行扩容操作
        threshold = (int) (capacity * loadFactor);
        //初始化table数组
        table = new Entry[capacity];
        init();
    }

看上图源码,每新建一个HashMap时都会初始化一个数组table,table数组中元素为Entry节点。

static class Entry<K,V> implements Map.Entry<K,V> {
        final K key;
        V value;
        Entry<K,V> next;
        final int hash;

        /**
         * Creates new entry.
         */
        Entry(int h, K k, V v, Entry<K,V> n) {
            value = v;
            next = n;
            key = k;
            hash = h;
        }
        .......
    }

 

 

 

 

CurrentHashMap:

  • 1)底层采用分段的segment数组+链表+红黑树实现,线程安全,高效的HashMap实现。
  • 2)通过把整个map分成N个segment,可以提供相同的线程安全,但是效率提升N倍,默认提升16倍,允许16个进程同时进行。(读操作不加锁,由于hash entry的value变量是volatile的,能保证读到的最新值)。
  • 3)CurrentHashMap允许多个改操作并发进行,其关键在于使用锁分离技术。
  • 4)有些方法需要跨段,比如size()和containsValue().
  • 5)扩容:段内扩容(段内元素超过该段对应的Entry数组长度的75%触发扩容,不会对整个map扩容),插入前检测不需要扩容,有效避免无效扩容。

 

CAS无锁算法

 

posted @ 2020-03-26 09:43  cow09  阅读(618)  评论(0)    收藏  举报