HashTatable和LinkedHashMap区别

HashTable

继承关系
extends Dictionary<K,V> JDK较早提供的实现类
默认值
数组初始化大小:11
加载因子:0.75
增长方式
2*table.length+1

CRUD

public synchronized V put(K key, V value) {
    // Make sure the value is not null
    if (value == null) {
        throw new NullPointerException();
    }

    // Makes sure the key is not already in the hashtable.
    Entry tab[] = table;
    //key不能为null,key为null也会抛出空指针问题
    //通过key的hash找到存储位置
    int hash = hash(key);
    int index = (hash & 0x7FFFFFFF) % tab.length;
    
    for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
        if ((e.hash == hash) && e.key.equals(key)) {
            V old = e.value;
            e.value = value;
            return old;
        }
    }

    modCount++;
    if (count >= threshold) {
        // Rehash the table if the threshold is exceeded
        rehash();

        tab = table;
        hash = hash(key);
        index = (hash & 0x7FFFFFFF) % tab.length;
    }

    // Creates the new entry.
    Entry<K,V> e = tab[index];
    tab[index] = new Entry<>(hash, key, value, e);
    count++;
    return null;
}

protected void rehash() {
    int oldCapacity = table.length;
    Entry<K,V>[] oldMap = table;

    // overflow-conscious code
    int newCapacity = (oldCapacity << 1) + 1;
    if (newCapacity - MAX_ARRAY_SIZE > 0) {
        if (oldCapacity == MAX_ARRAY_SIZE)
            // Keep running with MAX_ARRAY_SIZE buckets
            return;
        newCapacity = MAX_ARRAY_SIZE;
    }
    Entry<K,V>[] newMap = new Entry[newCapacity];

    modCount++;
    threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
    boolean rehash = initHashSeedAsNeeded(newCapacity);

    table = newMap;

    for (int i = oldCapacity ; i-- > 0 ;) {
        for (Entry<K,V> old = oldMap[i] ; old != null ; ) {
            Entry<K,V> e = old;
            old = old.next;

            if (rehash) {
                e.hash = hash(e.key);
            }
            int index = (e.hash & 0x7FFFFFFF) % newCapacity;
            e.next = newMap[index];
            newMap[index] = e;
        }
    }
}

HashTable的put过程
1、判断value不能为null,若为null抛出异常-》hashtable中value不能为null
2、通过key进行hash获取到key该存储的索引位置
3、该索引位置的链表进行遍历,获取key是否存在(key存在条件 hash相等且通过key.equals判断相等)
4、在存在该key的情况下,将value值进行更新且直接返回
5、key不存在则进行新节点插入逻辑
5.1、扩容考虑:entry节点个数大于阈值 (count>threshold)进行扩容
5.2、新容量大小为:2*table.length+1
5.3、将原哈希表中的数据全部进行重新hash到新的hash表中
5.4、更新插入的key的新的位置
5.5、找到新节点位置,创建entry实体通过头插入将元素插入

1.get操作获取元素

public synchronized V get(Object key) {
    Entry tab[] = table;
    int hash = hash(key);
    int index = (hash & 0x7FFFFFFF) % tab.length;
    for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
        if ((e.hash == hash) && e.key.equals(key)) {
            return e.value;
        }
    }
    return null;
}

特点:get方法本身也具有线程安全性
get过程:
1、通过key来hash获取到存储索引位置-》通过key为null进行get操作也会抛出异常
2、遍历当前索引位置节点,判断是否相等(hash、equals),找到则直接返回value值
3、未找到则返回null

2.remove()删除操作

public synchronized V remove(Object key) {
    Entry tab[] = table;
    int hash = hash(key);
    int index = (hash & 0x7FFFFFFF) % tab.length;
    for (Entry<K,V> e = tab[index], prev = null ; e != null ; prev = e, e = e.next) {
        if ((e.hash == hash) && e.key.equals(key)) {
            modCount++;
            if (prev != null) {
                prev.next = e.next;
            } else {
                tab[index] = e.next;
            }
            count--;
            V oldValue = e.value;
            e.value = null;
            return oldValue;
        }
    }
    return null;
}

删除操作本身也是线程安全的
删除过程:
1、通过key获取到当前存储的索引位置
2、对该索引位置链表进行遍历,找到key锁对应的entry实体、并进行删除

HashMap和HashTable的异同点
相同点:
1、底层数据结构都为数组+链表
2、key都不能重复
3、插入元素有不能保证插入有序
4、哈希过程通过key进行hash
不同点:
1、安全性问题:
HashMap不能保证线程安全
HashTable能保证线程
2、继承关系:
HashMap继承自AbstractMap
HashTable继承自Dictionary
3、null值问题
HashMap的key和value都可以为null
HashTable的key和value都不能为null
4、扩容方式
HashMap按照2table.length
HashTable按照2
table.length+1
5、默认值
HashMap默认数组大小为16
HashTable默认数组大小为11
6、hash算法不同
7、效率不同
HashMap在单线程小效率高
HashTable在单线程小效率低

HashMap不具有线程安全性,如何让其在多线程下具有安全性呢?
使用集合工具类Collections使HashMap具有线程安全性,Collections.synchronizedMap

HashTable能保证线程安全原理
HashTable对相应方法添加Synchronized关键字,该关键字是一种互斥锁互斥锁的目的是保证同一时刻只能有一个线程对资源的访问在HashMap对put、get等一般方法添加Synchronized关键字,修饰的是类对象,该对象调用put操作即为该HashTable对象添加了一个互斥锁,那么在同一时刻只能一个线程访问该HashTable,从而保证添加元素不会出现异常LinkedHashMap

linkedHashMap
通过以上几点来研究LinkedHashMap
找到与HashMap不同的点
1、继承关系不同:LinkedHashMap继承自HashMap
2、Entry节点不同:before\after
3、新增属性:header、accessOrder; 顺序性:true:访问有序 false:插入有序 默认:false插入有序

LinkedHashMap 特点?
LinkedHashMap存在的必要性??->hashMap存在还需要LinkedHashMap的作用
通过底层数据结构来保证数据的有序性
CRUD

LinkedHashMap的put过程

(父类HashMap实现)

    public V put(K key, V value) {
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key);
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
    
        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }
    (当前类LinkedHashMap实现)
    void addEntry(int hash, K key, V value, int bucketIndex) {
        super.addEntry(hash, key, value, bucketIndex);
    
        // Remove eldest entry if instructed
        Entry<K,V> eldest = header.after;
        if (removeEldestEntry(eldest)) {
            removeEntryForKey(eldest.key);
        }
    }
    (父类HashMap实现)
    void addEntry(int hash, K key, V value, int bucketIndex) {
        if ((size >= threshold) && (null != table[bucketIndex])) {
            resize(2 * table.length);
            hash = (null != key) ? hash(key) : 0;
            bucketIndex = indexFor(hash, table.length);
        }
    
        createEntry(hash, key, value, bucketIndex);
    }
    
    (当前类LinkedHashMap实现)
    void createEntry(int hash, K key, V value, int bucketIndex) {
        HashMap.Entry<K,V> old = table[bucketIndex];
        Entry<K,V> e = new Entry<>(hash, key, value, old);
        table[bucketIndex] = e;
        e.addBefore(header);
        size++;
    }
    (当前类LinkedHashMap实现)
    private void addBefore(Entry<K,V> existingEntry) {
        after  = existingEntry;
        before = existingEntry.before;
        before.after = this;
        after.before = this;
    }

未完待续…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值