数组
- 特点
数组是最基础的数据结构,它会在初始化申请一块连续的固定的内存区域。所以数组的访问速度很快,时间复杂度是O(1),但是由于是固定的区域,所以数组的长度在初始化之后是无法修改的。 - Arrays
Arrays是一个便于操作数组的类- sort:为数组排序
- binarySearch:在数组中进行二分查找
- equals:比较两个数组
- fill:为数组的每一个元素填充数据
- copyOf/copyOfRange,复制一个数组
- asList:将数组转化为List,注意,返回的list是不可修改的只读的,使用add,remove,clear都是无效的。
- toString:把数组转化为字符串,用于打印输出。
ArrayList
- 特点
ArrayList是可变长的数组,实际上当长度不够时,会重新申请一段更大的内存区域用于存放更大的数组。当对ArrayList进行插入和删除的时候,需要对数组其他元素进行移动,代价比较高,因此,ArrayList适合于遍历查询,而不适合插入和删除。 - 扩容:每次扩容增长约1.5倍,添加第一个元素时,默认未10个元素
//扩容代码
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
其中
int newCapacity = oldCapacity + (oldCapacity >> 1);
就是扩容的语句,实际上是原长度+原长度右移一位(相当于除以2).
那为什么是1.5倍呢,大概是如果是2倍的话增长太快,而1.5刚好可以转化为以上的位运算。
扩容非常影响性能,所以如果是已知数据的大约长度,应该设置一个差不多的初始值,避免多次扩容。
List<Integer> arr = new ArrayList<>(10000);
Vector与CopyOnWriteArrayList
- Vector特点
Vector和ArrayList一样,只不过Vector支持线程同步,大部分方法都使用synchronized修饰,所以其访问比较慢。
但是Vector不被建议使用,其效率类似于HashTable,都是比较低,可以使用 CopyOnWriteArrayList,CopyOnWriteArrayList使用读写锁的模式。当读的时候,是共享的,任意线程都可以读,但是不能写,当写的时候是排他的,只能一个线程写。这种方式即实现多线程同步的问题,也没有损耗太多的性能。
LinkedList
- 特点
LinkedList链表,区别于上面的三种,链表并不申请固定的内存,而是增加一个前驱和后驱的指针,用于查找下一个和上一个元素。因此其查找的时间复杂度时O(n),但是其插入和删除的时间复杂度为O(1),只要修改前后驱的指针就可以了。 - 堆栈
List同时也实现了堆栈的功能- push,压入数据
- pop,弹出栈顶的数据
- peek,返回栈顶数据,但是不删除
- 双头队列
同时也实现了双头队列的功能- getFirst:获取队头
- getLast:获取队尾
- removeFirst:移除队尾
- removeLast:移除队头
- addFirst:添加到队头
- addLast:添加到队尾
Collections
ArrayList,Vector,LinkedList都实现了List接口,Collections是List接口的一个辅组类,提供以下函数
- binarySearch:二分查找
- swap:交换元素
- sort:排序
- copy:复制
- fill:填充
fail-fast和fail-safe
当对集合进行遍历时,如果同时修改集合,可能会导致不可预知的后果,为了防止这种后果,java禁止了在循环中修改被循环的对象,这就是快速失败(fail-fast).但是对于多线程的情况下,快速失败会导致性能的问题。
于是,在支持多线程的数据结构中,当对集合进行遍历时,对数据进行修改时,修改的是复制的集合,因此不会触发fail-fast,称之为fail-safe.
fail-safe的缺点是
- 无法感知被修改的集合,因为遍历的还是未被修改的集合。
- 需要产生大量额复制对象。
Enumeration 和 Iterator的区别
Enumeration 和 Iterator都用于迭代,但是Enumeration未实现fail-fast机制,因此Enumeration更快,但是不安全。