Java中ArrayList集合数据结构:数组
顺序存储,Random Access(Direct Access)这种方式,相邻的数据元素存放于相邻的内存地址中,整块内存地址是连续的,可以根据元素的位置直接计算出内存地址,直接进行读取。对于顺序存储读取一个特定位置元素的平均时间复杂度为O(1),遍历整个集合的平均时间复杂度为O(n)。正常来说,只有基于数组实现的集合,才有这种特性。
1.传统的for循环遍历,基于计数器的:
遍历者自己在集合外部维护一个计数器,然后依次读取每一个位置的元素,当读取到一最后一个元素后停止。主要是需要按元素的位置来读取。这也是最原始的集合遍历方法。
代码实现:
public static void forTest(List<String> list){
int size = list.size();
for(int i=0;i<size;i++) {
String v = list.get(i);
}
}
反编译字节码:javap -c xxx.calsss
源码分析:new ArrayList().get()
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
E elementData(int index) {
return (E) elementData[index];
}
从代码分析 ArrayList for遍历是通过数组下标获取数据
2.foreach 循环遍历:
屏蔽了显式声明的Iterator和计数器。内部也是采用了Iterator的方式实现,只不过Java编译器帮我们生成了这些代码
Iterator本来是OO的一个设计模式,主要目的是屏蔽不同数据集合的特点,统一遍历集合的接口。Java作为一个OO语言,自然也在Collections中支持了Iterator模式。相比于传统for循环,Iterator取缔了显式的遍历计数器。所以基于存储集合的Iterator可以直接按位置访问数据。
优点:代码简洁,不易出错
缺点:只能做简单的遍历,不能在遍历过程中(删除、替换)数据集合
代码实现:
public static void forEach(List<String> list){
for(String str : list){
String v = str;
}
}
反编译字节码:javap -c xxx.calsss
源码解析:forEache实现new ArrayList().iterator().next()
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
//省略其他内部代码
}
从代码分析 ArrayList forEach遍历 是通过Iterator迭代器遍历数据,也是通过数组下标获取数据
ArrayList for、forEach 遍历测试结果如下:(测试结果仅供参考)
分析结果:整体来看性能差异不大,遍历次数越少 for 性能高于 forEach
遍历次数越多 forEach性能高于 for