Java集合的继承关系图
一、集合Collection接口
继承Iterable接口,两个子接口为List和Set接口
二、集合和数组的区别
1、数组是Java语言内置的数据类型,他是一个线性的序列,而且在生命周期也是不能改变的,还有JAVA数组会做边界检查,如果发现有越界现象,会报RuntimeException异常错误,当然检查边界会以效率为代价。
2、数组可以存储基本数据类型和引用数据类型,基本数据类型储存的是值;引用数据类型存储的是引用地址。
3、集合只能存储引用数据类型,存储的是引用数据类型的地址值,如果集合要存储基本数据类型,会自动装包。
4、集合容器的大小是可变的;数组是固定不变的。
5、数组只能存储类型相同的数据,集合可以存储类型不同的数据。
6、数组执行效率或类型检查都是最快的
三、List集合
List是有序、可重复的容器。
有序:List中每个元素都有索引标记。可以根据元素的索引标记(在List中的位置)访问元素,从而精确控制这些元素
可重复:List允许加入重复的元素。更加确切地讲,List通常允许满足e1.equals(e2)的元素重复加入容器
List的几个子类:ArrayList、LinkedList、Vector
1、ArrayList集合
1.1 ArrayList底层为对象数组。
1.2 查询效率高,增删效率低,线程不安全
1.3 ArrayList自动扩容实现原理:
默认长度是10,如果满了(当size==length)开始扩容;扩容以现长度的一半进行扩容。
具体做法是新建一个目的长度的数组然后将原数组的数据注意拷贝过去,然后将原数组回收
1.4 remove底层其实也是数组拷贝,将要删除的对象去掉之后拷贝其他值
2、LinkedList集合
2.1 LinkedList底层为双链表
2.2 查询效率低,增删效率高,线程不安全
2.3 主要参数,如下:
public class LinkdeList<E>{
private transient Entry<E> header = new Entry<E>(null,null,null);
private transient int size = 0;
}
private static class Entry<E>(){
E element;
Entry<E> next;
Entry<E> previous;
}
2.4 向容器中添加元素时会建立pre和next的连接
3、Vector集合
底层数组实现以List实现。每个方法都添加了线程锁synchronized;底层实现和List差不多。
四、Map接口
Map就是用来储存“键(key)-值(value)对“的。Map主要通过键来标识,所以“键对象”不能重复
主要实现类有HashMap、TreeMap、HashTable等。
Map的键是否重复通过equals方法实现
1、HashMap
1.1HashMap底层实现使用了哈希表
哈希表的底层实际上是一个链表+数组的组合,结合了数组和链表的优点(查询快、增删也快)
底层是一个节点数组,数组是一个单向链表
1.2、存储方法:
先计算key值的hashcode,然后根据数组长度进行散列存储
简单的计算hash散列的算法:
1、hash=hashcode%数组长度
2、hash = hashcode&(数组长度-1)数组长度必须为2的整数幂
同一个对象的hashcode必须相同
1.3、hashmap的位桶数组初始大小为16,如果达到(0.75*数组长度),则扩大二倍
1.4、在jdk8中,链表新增到数量大于8的情况会变为红黑树,为了避免在链表数增减为8的时候频繁转换。当红黑树的节点少于6的时候才会由红黑树转换为链表
1.5、如果key值与前面相同的话,会覆盖前面的值
2、TreeMap
2.1、TreeMap是红黑二叉树的典型实现。
3、HashTable
HashMap和HashTable的区别:
相同点:都实现了同时实现 了map、Cloneable(可复制)、Serializable(可序列化)这三个接口。
不同点:
- 父类不同:HashMap是继承自AbstractMap类,而Hashtable是继承自Dictionary类。
- 对null的支持不同:HashMap允许key或者value为null,HashTable不允许key或者value为null
- 安全性不同:HashMap线程不安全,执行效率高;HashTable线程安全,执行效率低
- 初始容量不同:HashMap的初始容量为16,Hashtable 初始容量为11,两者的负载因子默认都是0.75
- 计算hash值的方法不同:HashMap添加元素时,是使用自定义的哈希算法;HashTable是直接采用key的hashCode()
- 底层实现方式不同:jdk1.7底层都是数组+链表,但jdk1.8 HashMap加入了红黑树
- 支持的遍历种类不同:HashMap只支持Iterator遍历;HashTable支持Iterator和Enumeration两种方式遍历
HashTable和ConcurrentHashMap线程安全的实现方式的区别:
HashTable是在每个方法上加了synchronized 关键字,所以执行效率低。
ConcurrentHashMap虽然也是线程安全的,但是它的效率比Hashtable要高好多倍。因为 ConcurrentHashMap使用了分段锁,并不对整个数据进行锁定。
五、set接口
set接口继承自Collection,Set接口中 没有新增的方法,方法和Collection保持完全一致。
set特点:无序、不可重复。无序指set中没有索引,我们只能遍历查找;不可重复指的是容器中不允许有重复元素,再添加时如果新元素和set中某个元素equals()方法对比为true,则不能加入;甚至set也只能有一个null元素
set常用实现类有:HashSet、TreeSet等
1、HashSet
HashSet采用的是哈希算法实现,底层实现是一个HashMap,查询效率和增删效率都比较高。
底层是一个value为常量,只有key存在的HashMap
2、TreeSet
底层是一个TreeMap。可以用来排序。底层是一个value为常量,只有key存在的TreeMap
六、线程安全的集合
1、如何选择线程安全的集合?
上文所说的Vector和HashTable虽然是线程安全的,但是它们都是利用非常粗粒度的同步方式(在方法上加synchronized),在高并发情况下,性能比较低下。所以java并发包提供了线程安全的容器类:
-
各种并发容器,比如ConcurrentHashMap、CopyOnWriteArrayList。
-
各种线程安全队列(Queue/Deque),如ArrayBlockingQueue、SynchronousQueue。
-
各种有序容器的线程安全版本等。
2、为什么需要ConcurrentHashMap?
我们知道除了HashTable因为性能问题无法满足我们在高并发场景下使用。为什么不能使用Collections提供的同步包装器来解决问题呢?
因为我们利用同步包装器只是构造出了另一种同步版本,虽然不再所有方法上声明synchronized关键字,但是还是利用了“this”作为互斥的mutex,没有真正意义上的改进!所以需要ConcurrentHashMap
private static class SynchronizedMap
implements Map, Serializable {
private final Map m; // Backing Map
final Object mutex; // Object on which to synchronize
// …
public int size() {
synchronized (mutex) {return m.size();}
}
// …
}
3、ConcurrentHashMap
参考链接:深入浅出ConcurrentHashMap详解_学哥斌的博客-CSDN博客_concurrenthashmap