常见的集合有哪些?
Java的集合类主要由两个根接口Collection和Map派生出来的,Collection派生出了三个子接口:List、Set、Queue,因此Java集合大致也可分成List、Set、Queue、Map四种接口体系。
注意:Collection是一个接口,Collections是一个工具类,Map不是Collection的子接口
- List代表了有序可重复集,可直接根据元素的索引来访问;
- Set代表了无序不可重复集合,只能根据元素本身来访问;
- Queue是队列集合;
- Map代表的是存储key-value对的集合,可根据元素的key来访问value;
它们的实现类分别有:ArrayList、LinkedList、ArrayQueue、HashSet、TreeSet、HashMap、TreeMap等实现类
线程安全的集合有哪些?线程不安全的呢?
线程安全的:
- Hashtable:比HashMap多了个线程安全
- ConcurrentHashMap:是一种高效但是线程安全的集合
- Vector:比ArrayList多了个同步化机制
- Stack:栈,也是线程安全的,继承于Vector
线程不安全的:
- HashMap
- ArrayList
- LinkedList
- HashSet
- TreeSet
- TreeMap
ArrayList和LinkedList的异同点?
相同点:
- ArrayList和LinkedList都是不同步的,也就是不保证线程安全
不同点:
- ArrayList的底层数据结构是数组,支持下标访问,查询数据快,默认初始值大小为10,容量不足时会进行扩容
- LinkedList的底层数据结构是链表,将元素添加到链表的末尾,无需扩容
ArrayList和LinkedList分别适用于哪些场景?
对于随机index访问的get和set方法,ArrayList的速度要优先于LinkedList,因为ArrayList直接通过数组下标找到元素;LinkedList要移动指针遍历每个元素直到找到为止
新增和删除元素,LinkedList的速度要优于ArrayList,因为ArrayList在新增和删除元素时,可能扩容和复制数组,而LinkedList的新增和删除操作只需要修改指针即可。
因此,ArrayList适用于查询多,增删少的场景,而LinkedList适用于查询少,增删多的场景
ArrayList和Vector的区别?
- Vector是线程安全的,ArrayList不是线程安全的。其中,Vector在关键性的方法前面都加了synchronized关键字,来保证线程的安全性。如果有多个线程会访问到集合,那最好是使用 Vector,因为不需要我们自己再去考虑和编写线程安全的代码。
- ArrayList在底层数组不够用时在原来的基础上扩展0.5倍,Vector是扩展1倍,这样ArrayList就有利于节约内存空间。
说一下ArrayList的扩容机制?
ArrayList扩容的本质就是计算出新的扩容数组的size后实例化,并将原有数组内容复制到新数组中去。然后把ArrayList的地址指向新数组,默认情况下,新的容量会是原容量的1.5倍。
以JDK1.8为例
public boolean add(E e) {
//判断是否可以容纳e,若能,则直接添加在末尾;若不能,则进行扩容,然后再把e添加在末尾
ensureCapacityInternal(size + 1); // Increments modCount!!
//将e添加到数组末尾
elementData[size++] = e;
return true;
}
// 每次在add()一个元素时,arraylist都需要对这个list的容量进行一个判断。通过ensureCapacityInternal()方法确保当前ArrayList维护的数组具有存储新元素的能力,经过处理之后将元素存储在数组elementData的尾部
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
//如果传入的是个空数组则最小容量取默认容量与minCapacity之间的最大值
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// 若ArrayList已有的存储能力满足最低存储要求,则返回add直接添加元素;如果最低要求的存储能力>ArrayList已有的存储能力,这就表示ArrayList的存储能力不足,因此需要调用 grow();方法进行扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// 获取elementData数组的内存空间长度
int oldCapacity = elementData.length;
// 扩容至原来的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
//校验容量是否够
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//若预设值大于默认的最大值,检查是否溢出
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 调用Arrays.copyOf方法将elementData数组指向新的内存空间
//并将elementData的数据复制到新的内存空间
elementData = Arrays.copyOf(elementData, newCapacity);
}
Array和ArrayList有什么区别?
- Array可以包含基本类型和对象类型,ArrayList只能包含对象类型
- Array大小是固定的,ArrayList的大小是动态变化的
- ArrayList提供了更多的方法和特性,比如:add(),reoveall(),iterator()等等
如何返回一个线程安全的List?
可以用Collections.synchronizedList()返回一个线程安全的List 或者用CopyOnWriteArrayList