HashMap和ConcurrentHashMap

HashMap和ConcurrentHashMap

首先我们先来聊聊集合框架!

一、集合框架

在这里插入图片描述

1、集合框架的关系

(1)Iterable、Collection、List、Set、Map是最基本的

(2)Queue(队列)、Deque(双端队列)、Stack(栈)

2、集合框架的具体实现类

(1)非线程安全的实现类:ArrayList、LinkedList、HashSet、TreeSet、HashMap、TreeMap

(2)同步的实现类:Vector、HashTable

(3)并发访问安全的实现类:CopyOnWriteArrayList、CopyOnWriteArraySet、ConcurrentHashMap(重点)

3、每个实现类的底层数据结构

ArrayList(数组)

LinkedList(链表)

HashMap(jdk1.7是数组+链表,jdk1.8是数组+链表+红黑树)

HashSet(基于HashMap实现,键就是元素,值就是new Object)

TreeMap(红黑树)

4、判断是否是同一个对象

值比较:equals()来进行判断。要求根据业务重写hashCode()和equals()。

5、迭代器
  1. Iterable接口下的实现类,都可以使用迭代器
  2. for循环操作底层也是使用的迭代器
  3. fail-fast(快速失败的迭代器):非线程安全的类使用的迭代器(在遍历操作的时候们只能使用迭代器修改元素,不使用迭代器修改元素,就会抛出异常),比如下面这段代码,test1()没使用迭代器进行修改元素,test2()使用迭代器进行修改,观察:
import java.util.*;

public class Main{
    static List<String> LIST = new ArrayList<>();
    static {
        LIST.add("A");
        LIST.add("B");
        LIST.add("C");
        LIST.add("C");
        LIST.add("D");
    }
    public static void test1(){
        int i = 0;
        for(String str : LIST){
            //遍历过程中,使用非迭代器的方式修改元素是不允许
            if(i==2)
                LIST.remove(2);
            else
                System.out.println(str);
            i++;
        }
        //遍历操作外修改是允许的
        //LIST.remove(2);
    }

    public static void test2() {
        int i = 0;
        Iterator<String> it = LIST.iterator();
        while(it.hasNext()){
            String str = it.next();
            if(i==2)
                it.remove();
            // LIST.remove(2);
            else
                System.out.println(str);
            i++;
        }
    }

    public static void main(String[] args) {
        // test1();
        test2();
    }
}

  1. fail-saft(安全失败的迭代器):CopyOnWriterArrayList、ConcurrentHashMap本身没有迭代器,但内部的属性(Entry、Node)具有迭代器
  2. HashMap内部的迭代器是fail-fast类型,ConcurrrentHashMap内部的迭代器是fail-saft类型。fail-saft支持在遍历操作时,修改元素
二、HashMap

前面的博客已经细致的讲解过了HashMap,没看过的小伙伴可以去看看。哈希表以及HashMap代码实现

如果 负载因子(size/数组长度)> 0.75,需要对数组进行扩容

hash冲突的问题:JDK实现是链地址法解决

使用场景:查询频率高,插入/删除频率低

可能产生的问题:环形链表(多个线程操作HashMap所导致的)

​ 因为HashMap添加元素分为两步:1、添加到头结点

​ 2、调换链表的位置

1、HashMap JDK1.7的实现

##### [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zupuLW7o-1585123084880)(C:\Users\惠秋丽\AppData\Roaming\Typora\typora-user-images\1585018277763.png)]2、HashMap JDK1.8的实现

2、HashMap JDK1.8的实现

在这里插入图片描述
JDK1.7:

多线程下的数据操作是不安全的,并且可能出现环形链表造成死循环(扩容时resize操作会改变链表中元素的顺序,从头部插入,多线程执行顺序的不确定性可能出现线程1将node1.next指向node2,而接着执行线程2将node2.next指向node1)

JDK1.8:

尽管jdk1.8修复了死循环问题,只有可能出现数据丢失的情况。多线程下还是建议使ConcurrrentHashMap

3、HashTable
  1. 底层数据结构:数组+链表
  2. 底层实现再put、get,内部大部分的方法都是使用synchronized修饰的
  3. 多线程操作下是安全的,但是效率太低了
  4. HashTable不能null键,null值
三、ConcurrentHashMap

1、底层数据结构以及原理

(1)jdk1.7是数组+链表,实现原理是Segment分段锁+ReentrantLock锁机制(segment数组+HashEntry数组+链表)

(2)jdk1.8是数组+链表+红黑树,实现原理基于CAS+synchronized机制

2、特性:

(1)不是把Map整个对象加锁,而是缩小了锁的范围(1.7是segment,1.8是node)

(2)可以在多线程环境下并发、并行执行不同segment/node的插入/删除操作。读操作无锁的,使用volatile关键字,保证线程安全

(3)1.7对同一个segment的操作是ReentrantLock.lock()/unlock()进行线程间的同步,1.8是Node的操作,如果node==null,适应CAS进行自旋尝试插入,如果node != null,synchronized(node)加锁,达到线程间安全。

ConcurrentHashMap1.7实现

在这里插入图片描述

ConcurrentHashMap1.8实现

在这里插入图片描述

四、HashMap、HashTable及ConcurrrentHashMap的区别

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PRhjKcTQ-1585123084885)(C:\Users\惠秋丽\AppData\Roaming\Typora\typora-user-images\1585021679820.png)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值