集合基础

集合

长度可变的对象容器,定义了对多个对象进行操作的常用方法。

  • Collection
    • List
    • Set
    • Queue
  • Map
    • HashMap
    • ConcurrentHashMap

List

ArrayList

  • 涉及扩容缩容问题,源码中默认初始容量为10.
  • 扩容倍数为1.5。

常见的迭代器并发异常

ArrayList<Integer> al = new ArrayList<>(11);
        al.add(1);
        al.add(2);
        al.add(3);
        for (Integer integer : al) {
            al.remove(1);
        }
//ArrayList里迭代器的next方法会在执行时比较游标和长度,删除或者增加后则会不相等抛出异常。
Exception in thread "main" java.util.ConcurrentModificationException

拷贝

拷贝栈中内容,栈中存在引用地址指向堆,所以为浅拷贝,不独立。

要实现ArrayList的深拷贝,需要将其中类重写clone方法

@Data
public class User implements Cloneable {
	//String类型默认深拷贝
    private String name;
    //基本类型只存值
    private int age;
    //其他类型都需要拷贝
    private int[] a;
    //Animal类里也要重写clone
    private Animal animal;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        User user = (User) super.clone();
        user.setA(a.clone());
        user.setAnimal((Animal)animal.clone());
        return user;
    }
}

序列化

ArrayList重写了序列化方法,删除不需要的空值和一些三方参数。

Arrays.asList()用的泛型,基本类型数组放入只会被存入ArraysList的0下标元素。

Vector

在ArrayList基础上粗暴的添加synchronized,不建议使用。

CopyOnWriteArrayList

修改之前会复制一份数组进行操作,写时加ReentrantLock锁,读不会阻塞,读写分离。
所有弱一致性,读取数据不为最新。

LinkedList

双向链表,实现了Deque接口,有操作头部尾部的api。

Set

HashSet

源码中构造函数使用的是HashMap,利于查找,因为只有k值

  • Key不能重复(所以只能一个空值)
  • Key是无序的
  • 非线程安全,没有get方法

阻塞队列BlockingQueue

ArrayBlockingQueue

静态数组,没有扩容机制。null占位。
一把锁,ReentrantLock锁的整个数组。
入队唤醒notEmpty,出队notFull。
takekIndex,putIndex保证先进先出。
读写互斥。

LinkedBlockingQueue

可以扩容
两把锁,读写各一把
head,last保证先进先出。
删除元素需要两把锁,读写锁。

LinkedBlockingDeque

在LinkedBlockingQueue基础上提供首尾多端操作。

PriorityQueue

优先队列,插入元素自动排好序。

Map

HashMap

(jdk7)Node数组+单向链表。

  • key为null,存在0位置。hash(key) = hashcode。
    hashcode % 数组长度即为存储下标。
  • hash冲突,不同key同一hash,则数组下标相同,所以在该下标上扩充链表,假如冲突次数过多,get时则会造成链表查询,效率低。

(jdk8) 当链表高度达到8,并且数组长度达到64,链表则会变成红黑树。保留链表的原因,红黑树扩容复杂。

  • 扩容负载因子:0.75。
  • 如果数组元素个数达到 因子*数组长度,会进行2倍扩容,重新hash。
  • jdk1.7扩容复制链表时使用头插法,会改变B.next = A,有闭链死锁风险,jdk1.8则尾插法。
  • 初始容量只能定义为2的倍数,默认为16,为了减少hash碰撞的概率。

红黑树

二叉查找树

  • 左子树上所有结点的值均小于或等于它的根结点的值。

  • 右子树上所有结点的值均大于或等于它的根结点的值。

  • 左、右子树也分别为二叉排序树。

查找所需最大次数为二叉查找树的高度。

缺陷:容易造成长短腿,例如插入7,6,5,4,3。

为了解决该问题,于是红黑树诞生了。

在二叉查找树特性的基础之上还有以下特性:

  1. 节点是红色或黑色。
  2. 根节点是黑色。
  3. 每个叶子节点都是黑色的空节点(NIL节点)。
  4. 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
  5. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。

红黑树
红黑树最长路径不会超过最短路径的两倍。

  • 左旋:右变根,根变左,右左变左右。
  • 右旋:左边根,根变右,左右变右左。
  • 变色:主要依据第4,5特性。
  • 左边不满足规则就右旋,右边不满足就左旋。

红黑树参考链接

HashTable

在HashMap整个数组上加synchronized锁,不建议使用。

transient关键字:
将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会被序列化。

ConcurrentHashMap

jdk1.7)ReentrantLock锁+Segment(k,v)(extends ReentrantLock)+HashEntry(Node)

  • Segment(k,v)数组指向Node数组,加锁只要给Segment加上分段锁。
  • Segment的引入,所以元素查询慢一点。
  • get不加锁,Node的value使用Volatile修饰,最新值。
  • Segment也是用Volatile修饰。

jdk1.8由于synchronized升级,又使用了synchronized+cas+Node+红黑树。查找使用CAS乐观锁。

  • 直接锁当前下标的node的head节点。
  • 扩容阻塞所有读写操作,也是新数组,但迁移以16为单位,多线程迁移。

TreeMap

红黑树实现,key从小到大存储。

LinkedHashMap

顺序保存,Node之间增加before,after指针维护顺序。

Volatile

当一个变量定义为 volatile 之后,将具备两种特性:
  1.保证此变量对所有的线程的可见性,这里的“可见性”,如本文开头所述,当一个线程修改了这个变量的值,volatile 保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新。但普通变量做不到这点,普通变量的值在线程间传递均需要通过主内存(详见:Java内存模型)来完成。

  2.禁止指令重排序优化。有volatile修饰的变量,赋值后多执行了一个“load addl $0x0, (%esp)”操作,这个操作相当于一个内存屏障(指令重排序时不能把后面的指令重排序到内存屏障之前的位置),只有一个CPU访问内存时,并不需要内存屏障;(什么是指令重排序:是指CPU采用了允许将多条指令不按程序规定的顺序分开发送给各相应电路单元处理)。

volatile 性能:
  volatile 的读性能消耗与普通变量几乎相同,但是写操作稍慢,因为它需要在本地代码中插入许多内存屏障指令来保证处理器不发生乱序执行

CAS

CAS原理

死锁与阻塞

  • 死锁:就是死了。
  • 阻塞:堵车,还有救。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值