java 各种遍历方式对比与总结
本人不才,最近优化项目代码,涉及到这一块,特发文章,仅作记录
一、Array
Array(数组)是基于索引(index)的数据结构,它使用索引在数组中搜索和读取数据是很快的。
Array获取数据的时间复杂度是O(1),但是要删除数据却是开销很大,因为这需要重排数组中的所有数据, (因为删除数据以后, 需要把后面所有的数据前移)
缺点: 数组初始化必须指定初始化的长度, 否则报错
二、list:
List—是一个有序的集合,可以包含重复的元素,提供了按索引访问的方式,它继承Collection。
List有两个重要的实现类:ArrayList和LinkedList
1. ArrayList
- ArrayList是List的基于数组的实现方式,可以看作是能够自动增长容量的数组。
- ArrayList由于是基于数组的,可进行随机读写,用下标遍历的方式是最快的
- ArrayList进行add()操作时,会先进行容量的判断,如果容量不足,会进行扩容(扩容到原来的1.5倍int newCapacity = (oldCapacity * 3)/2+1)
-
进行扩容时,会进行大量的数组复制的操作,和申请新的连续空间
- 数组复制时,调用的是System.arraycopy()方法,效率相当的高
- ArrayList的默认初始化容量大小是10
-
初始化ArrayList时,尽量判断使用场景的数据量,指定合适的初始长度,减少ArrayList的扩容操作,可有效的提高运行效率
- 长度尽量使用2的幂作为长度, 计算机分配空间大都使用次幂去分配, 减少碎片空间
2. LinkedList
- LinkedList是list的基于双向链表的实现方式
- 链表不需要连续的空间,大小不确定
- 注意:LinkedList插入效率高,但每次的元素增加都需要新增一个Entry对象,并进行更多的赋值操作,在频繁的调用中,会对系统性能产生一定的影响,如果进行的都是插入到list尾部的操作,在ArrayList不进行扩容操作,追加操作的效率还是比LinkedList高的。
- 使用LinkedList对堆内存和gc的要求更高
- LinkedList查找元素的时候,因为数据结构是双向链表,用的是二分法,元素是在中间部分时,查找性能最低
添加和删除元素,由于不需要进行数组移动和复制操作,尤其是在任意位置插入和删除时,性能与ArrayList有本质的区别。
3.总结
进行遍历和追加操作时,尽量使用指定了初始长度的ArrayList,当需要在随机位置进行增加和删除操作时,可以使用linkedlist。
三、遍历方式
1.for
ArrayList<Integer> a =new ArrayList<>(20000);
int size = a.size();
for(int i=0; i<size;i++){
a.add(i);
}
2.foreach
for(int i : a){
System.out.println(i);
}
注意:
增强型for循环 底层用的是迭代器,跟第三种遍历方式没有本质区别
在使用增强型for循环不支持遍历时删除元素
使用增强型for循环时,对遍历的集合需要做null判断,不然可能引发空指针异常。
3.Iterator
Iterator iter = a.iterator();
while(iter.hasNext()){
Object o = iter.next();
}
迭代器遍历方式, 适用于连续内存存储方式,比如数组、 ArrayList(其实 ArrayList底层实现也是数组形式)。
缺点是只能从头开始遍历, 优点是可以边遍历边删除。
4.Lambda
a.parallelStream().forEach((s)->{
System.out.println(i);
});
a.stream().forEach((s)->{
System.out.println(i);
});
Lambda是jdk 1.8的新特性,手写优雅与方便
parallelStream()是多线程的,数据量小的时候,由于“jdk的开光”,性能还不如普通for循环
当数据量大和循环体操作比较复杂费时的时候,更建议使用parallelStream(),但要注意线程安全