String
String类用final修饰,不能被继承。字符串存储到字节数组里面,字节数组也被final修饰,也就是String一经赋值,就不能再被修改。像replace等修改接口,也是返回新的String实例对象。
ArrayList
数组数据结构。数据初始容量是10,并用size记录当前数组个数。当添加的数据超过数据容量后,会按照当前数组个数的1/2进行扩展容量,通过Arrays.copyof生成新的数组。
ArrayList有个modCount修改次数属性,当进行增加、删除操作时,ModCount都会+1。
当使用迭代器iterator时,会先记录modCount值,当使用迭代器进行操作时,会比较记录的modCount值与真实的modCount值,如果不一致,则抛出异常。比较不是线程安全的
ArrayList随机访问效率要比LinkedList高,通过下标能够直接访问。LinkedList要移动指针才能访问。
插入、删除时,LinkedList效率要比ArrayList高
数组是插入、删除慢,随机访问效率高
链表是插入、删除快,随机访问效率慢
LinkedList
链表数据结构。封装的有个Node内部类(元素,前一个节点引用,后一个节点引用),同样的数据量,LinkedList占用的空间要比ArrayList高,LinkedList还要存储引用。
HashMap
数组+链表+红黑树(JAVA8新增的)数据结构,数组里存放的Node节点:
- 当添加<Key,Value>时,会先根据Key计算hash值(hash值计算会用到数组容量,当数组进行扩容时,会重新计算hash值,找到对应的下标),再根据hash值算出对应的数组下标。由于数组每个元素可以看成是一个链表的头节点,这个时候还要去比较Key是否在链表里已经存在,存在的话,替换掉原有的键值对。如果是红黑树结构的话,就不用比较Key,直接插入就行。
- LOAD_FACTOR加载因子:当数组元素个数达到总个数跟加载因子的乘积以后,就会resize扩大数组的容量。扩大数组容量的好处是减少哈希冲突,缺点就是空间利用率降低。加载因子越大,哈希冲突的可能性就越大,空间利用率就越高。加载因子默认是0.75
- 扩容过程:
- 每次扩容都是原来的2倍
- 重新计算Key的hash值
- Key和Value都可以为null
HashSet
HashSet底层是HashMap结构,添加数据时,执行map.put(e, PRESENT)==null语句,其中PRESENT是new Object()。无序的
List、Set、Map区别
List是基于数组或链表数据结构实现的
Map是基于数组+链表+红黑树结构实现的
Set是基于Map实现的,无序,值是Map的KEY
线程安全集合
CopyOnWriteArrayList:通过ReentrantLock(公平锁)加锁,实现线程安全。会先拷贝数组数据到新的数组,对新的数组操作,没有记录修改的次数。
CopyOnWriteArraySet:底层是通过CopyOnWriteArrayList实现的,如果添加的数据存在,则不添加
ConcurrentHashMap:通过Synchronized加锁,悲观锁。实现线程安全。ConcurrentHashMap的锁又被称为分段锁,其实锁着的是数据元素,也就是链表的头节点,其他的数据元素没加锁。
ConcurrentLinkedQueue:通过CAS,乐观锁
队列
- 添加元素方法
- Add:底层调用的是offer方法
- Offer:将元素加到队尾,如果是优先级队列,还要做个排序
- Peek:获取队列第一个元素
- 删除元素方法
- Poll:删除队列元素,并返回
- Remove:
PriorityQueue-优先级队列
- 优先级队列元素默认按照优先级升序排序,优先级最小的排在第一位
- 数组数据结构
- 线程不安全
ArrayBlockingQueue-阻塞队列
数组数据结构,有ReentrantLock(公平锁),当通过offer方法添加数据时,会通过conditon唤醒阻塞线程,通过take方法获取数据时,如果队列为空,会通过condition阻塞线程,线程安全。
LinkedBlockingQueue-链表队列
链表数据结构,有ReentrantLock(公平锁)。Offer方法和take方法通过condtion做线程唤醒和阻塞控制。线程安全。
注意:BlockingQueue阻塞队列都是线程安全的