专题三、ArrayList遍历方式以及效率比较
一、遍历方式
ArrayList支持三种遍历方式。
1、第一种,随机访问,它是通过索引值去遍历
由于ArrayList实现了RandomAccess接口,它支持通过索引值去随机访问元素。
代码如下:
// 基本的for
for (int i = 0; i < size; i++)
{
value = list.get(i);
}
2、第二种,foreach语句
for (Integer integer : list)
{
value = integer;
}
3、第三种,Iterator迭代器方式
迭代器是一种模式,它可以使得对于序列类型的数据结构的遍历行为与被遍历的对象分离,即我们无需关心该序列的底层结构是什么样子的。只要拿到这个对象,使用迭代器就可以遍历这个对象的内部。
代码如下:
for (Iterator<Integer> iterator = list.iterator(); iterator.hasNext();)
{
value = iterator.next();
}
二、几种遍历方式效率的比较
要想知道上面几种遍历方式的效率如何,最简单的办法,就是我们自己编写代码来测试它。
测试代码如下:
/**
* 测试ArrayList中几种循环的效率
*
* @author Administrator
* @version 1.0
*/
public class TestArrayListLoop
{
public static void main(String[] args)
{
// 准备数据阶段
List<Integer> list = new ArrayList<Integer>();
for (int i = 0; i < 100000; i++)
{
list.add(i);
} // 测试阶段
int runCounts = 1000; // 执行次s数
int listSize = list.size();
int value;
// For循环的测试
long startTime1 = System.currentTimeMillis();
for (int i = 0; i < runCounts; i++)
{
loopOfFor(list);
}
long endTime1 = System.currentTimeMillis();
// Foreach循环的测试
long startTime2 = System.currentTimeMillis();
for (int i = 0; i < runCounts; i++)
{
loopOfForeach(list);
}
long endTime2 = System.currentTimeMillis();
// Iterator迭代器的测试
long startTime3 = System.currentTimeMillis();
for (int i = 0; i < runCounts; i++)
{
loopOfIterator(list);
}
long endTime3 = System.currentTimeMillis();
System.out.println("loopOfFor: " + (endTime1-startTime1)+ "ms");
System.out.println("loopOfForeach: "+ (endTime2-startTime2)+ "ms");
System.out.println("loopOfIterator: "+ (endTime3-startTime3)+ "ms");
} /**
* 由于ArrayList实现了RandomAccess接口,它支持通过索引值去随机访问元素。
* @param list
*/
public static void loopOfFor(List<Integer> list)
{
int value;
int size = list.size();
// 基本的for
for (int i = 0; i < size; i++)
{
value = list.get(i);
}
}
/**
* 使用forecah方法遍历数组
* @param list
*/
public static void loopOfForeach(List<Integer> list)
{
int value;
// foreach
for (Integer integer : list)
{
value = integer;
}
}
/**
* 通过迭代器方式遍历数组
* @param list
*/
public static void loopOfIterator(List<Integer> list)
{
int value;
// iterator
for (Iterator<Integer> iterator = list.iterator(); iterator.hasNext();)
{
value = iterator.next();
}
}
}
loopOfFor: 72ms
loopOfForeach: 89ms
loopOfIterator: 91ms
loopOfFor: 70ms
loopOfForeach: 90ms
loopOfIterator: 87ms
loopOfFor: 668ms
loopOfForeach: 760ms
loopOfIterator: 679ms
loopOfFor: 672ms
loopOfForeach: 751ms
loopOfIterator: 678ms
三、效率分析
1、为什么基本的for循环效率高于Iterator遍历?
As a rule of thumb, a List implementation should implement this interface if, for typical instances of the class, this loop:
for (int i=0, n=list.size(); i < n; i++)
list.get(i);
runs faster than this loop:
for (Iterator i=list.iterator(); i.hasNext(); )
i.next();
- 1、可以快速随机访问集合。
- 2、使用快速随机访问(for循环)效率可以高于Iterator。
2、为什么foreach循环效率与Iterator效率有点暧昧?
public Iterator<E> iterator() {
return new Itr();
}
从中,我们可以大致得出一个结论:foreach不是关键字,它的关键字是for,它的语句是由iterator实现的。
public Iterator<E> iterator()
{
return new Itr();
}
// An optimized version of AbstractList.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];
} public void remove()
{
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try
{
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex)
{
throw new ConcurrentModificationException();
} } @Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer)
{
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size)
{
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
{
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount)
{
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
} final void checkForComodification()
{
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
四、扩展
1、基本的for循环的效率一定比iterator迭代器的高吗?
- ArrayList实现了RandomAccess随机访问接口,因此它对随机访问的速度快,而基本的for循环中的get()方法,采用的即是随机访问的方法,因而在ArrayList中,for循环速度快。
- LinkedList采取的是顺序访问方式,iterator中的next()方法,采用的即是顺序访问方法,因此在LinkedList中,使用iterator的速度较快。
LinkedList中的结论正确吗?我们做个实验,测试一下就会水落石出。
代码如下:
public class TestLinkedListLoop
{
public static void main(String[] args)
{
// 准备数据阶段
List<Integer> list = new LinkedList<Integer>();
for (int i = 0; i < 10000; i++)
{
list.add(i);
} // 测试阶段
int runCounts = 10; // 执行次s数
int listSize = list.size();
int value;
// For循环的测试
long startTime1 = System.currentTimeMillis();
for (int i = 0; i < runCounts; i++)
{
loopOfFor(list);
}
long endTime1 = System.currentTimeMillis();
// Foreach循环的测试
long startTime2 = System.currentTimeMillis();
for (int i = 0; i < runCounts; i++)
{
loopOfForeach(list);
}
long endTime2 = System.currentTimeMillis();
// Iterator迭代器的测试
long startTime3 = System.currentTimeMillis();
for (int i = 0; i < runCounts; i++)
{
loopOfIterator(list);
}
long endTime3 = System.currentTimeMillis();
System.out.println("loopOfFor: " + (endTime1-startTime1)+ "ms");
System.out.println("loopOfForeach: "+ (endTime2-startTime2)+ "ms");
System.out.println("loopOfIterator: "+ (endTime3-startTime3)+ "ms");
} /**
* 由于ArrayList实现了RandomAccess接口,它支持通过索引值去随机访问元素。
* @param list
*/
public static void loopOfFor(List<Integer> list)
{
int value;
int size = list.size();
// 基本的for
for (int i = 0; i < size; i++)
{
value = list.get(i);
}
}
/**
* 使用forecah方法遍历数组
* @param list
*/
public static void loopOfForeach(List<Integer> list)
{
int value;
// foreach
for (Integer integer : list)
{
value = integer;
}
}
/**
* 通过迭代器方式遍历数组
* @param list
*/
public static void loopOfIterator(List<Integer> list)
{
int value;
// iterator
for (Iterator<Integer> iterator = list.iterator(); iterator.hasNext();)
{
value = iterator.next();
}
}
}
loopOfFor: 332ms
loopOfForeach: 5ms
loopOfIterator: 4ms
- for循环适合访问顺序存储结构,可以根据下标快速获取指定元素(即支持随机访问)。
- 而Iterator 适合访问链式存储结构,因为迭代器是通过next()和Pre()来定位的,但它也可以访问顺序存储结构的集合。
2、for、foreach、iterator之间的差别
1)形式差别
2)条件差别
3)多态差别
public void display(Iterator<object> it)
{
while(it.hasNext())
{
system.out.print(it.next()+"");
}
}
当我们需要遍历不同的集合时,我们只需要传递集合的iterator(如arr.iterator())看懂了吧,这就是iterator的好处,他不包含任何有关他所遍历的序列的类型信息,能够将遍历序列的操作与序列底层的结构分离。迭代器统一了对容器的访问方式。这也是接口的解耦的最好体现。
4)用法差别
for循环:一般用来处理比较简单的有序的,可预知大小的集合或数组
foreach:可用于遍历任何集合或数组,而且操作简单易懂,他唯一的不好就是需要了解集合内部类型
iterator:是最强大的,它可以随时修改或者删除集合内部的元素,并且是在不需要知道元素和集合的类型的情况下进行的(原因可参考第三点:多态差别),当你需要对不同的容器实现同样的遍历方式时,迭代器是最好的选择!
参考:
3、Java迭代器(转)(iterator详解以及和for循环的区别)
专题三、ArrayList遍历方式以及效率比较的更多相关文章
- Java中List集合的三种遍历方式(全网最详)
List集合在Java日常开发中是必不可少的,只要懂得运用各种各样的方法就可以大大提高我们开发的效率,适当活用各种方法才会使我们开发事半功倍. 我总结了三种List集合的遍历方式,下面一一来介绍. 首 ...
- set的三种遍历方式-----不能用for循环遍历(无序)
set的三种遍历方式,set遍历元素 list 遍历元素 http://blog.csdn.net/sunrainamazing/article/details/71577662 set遍历元素 ht ...
- for 、foreach 、iterator 三种遍历方式的比较
习惯用法 for.foreach循环.iterator迭代器都是我们常用的一种遍历方式,你可以用它来遍历任何东西:包括数组.集合等 for 惯用法: List<String> list = ...
- 基于Java的二叉树的三种遍历方式的递归与非递归实现
二叉树的遍历方式包括前序遍历.中序遍历和后序遍历,其实现方式包括递归实现和非递归实现. 前序遍历:根节点 | 左子树 | 右子树 中序遍历:左子树 | 根节点 | 右子树 后序遍历:左子树 | 右子树 ...
- Map三种遍历方式
Map三种遍历方式 package decorator; import java.util.Collection; import java.util.HashMap; import java.util ...
- 大数据学习day13------第三阶段----scala01-----函数式编程。scala以及IDEA的安装,变量的定义,条件表达式,for循环(守卫模式,推导式,可变参数以及三种遍历方式),方法定义,数组以及集合(可变和非可变),数组中常用的方法
具体见第三阶段scala-day01中的文档(scala编程基础---基础语法) 1. 函数式编程(https://www.cnblogs.com/wchukai/p/5651185.html): ...
- Java中list对象的三种遍历方式
1.增强for循环 for(String str : list) {//其内部实质上还是调用了迭代器遍历方式,这种循环方式还有其他限制,不建议使用. System.out.println(str); ...
- java map遍历方式及效率
本文转载自Java Map遍历方式的选择. 只给出遍历方式及结论.测试数据可以去原文看. 如果你使用HashMap 同时遍历key和value时,keySet与entrySet方法的性能差异取决于ke ...
- Map的三种遍历方式
对于Map的三种方式遍历 1.keySet() 2.values() 3.entrySet()三种方式得到Set之后,都可以使用 foreach或者iterator, 不能使用for,因为数据结构决定 ...
随机推荐
- 【不积跬步,无以致千里】Linux下启动Oracle服务和监听程序
oracle数据库是重量级的,其管理非常复杂,将其在linux平台上的启动和关闭步骤整理一下. 安装完毕oracle以后,需要创建oracle系统用户,并在/home/oracle下面的.bash_p ...
- 【转贴】gdb中的信号(signal)相关调试技巧
一篇不错的帖子,讲的是gdb中的信号(signal)相关调试技巧 转自Magic C++论坛 http://www.magicunix.com/index_ch.html http://www.m ...
- 假设有两个包含整数的vector对象,编写一段程序,检验其中一个vector对象是否是另一个的前缀。
#include<iostream> #include<string> #include<vector> using namespace std; int main ...
- Retina屏下1px border
layout tltle tags post ios7下移动web开发的几个坑 webapp 1.Retina屏下1px border 由于高清屏的特性,1px是由2×2个像素点来渲染,那么我们样式上 ...
- 列式存储 V.S. 行式存储
列式数据库 http://zh.wikipedia.org/wiki/%E5%88%97%E5%BC%8F%E6%95%B0%E6%8D%AE%E5%BA%93 列式存储与行式存储 http://my ...
- 信号之alarm和pause函数
使用alarm函数可以设置一个计时器,在将来某个指定的时间,该计时器会超时.当计时器超时时,产生SIGALRM信号.如果不忽略或不捕捉此信号,则其默认动作是终止调用该alarm函数的进程. #incl ...
- PAT 1011
1011. World Cup Betting (20) With the 2010 FIFA World Cup running, football fans the world over were ...
- iOS 常见知识点(三):Lock
iOS 常见知识点(一):Runtime iOS 常见知识点(二):RunLoop 锁是最常用的同步工具.一段代码段在同一个时间只能允许被有限个线程访问,比如一个线程 A 进入需要保护代码之前添加简单 ...
- View绘制详解(四),谝一谝layout过程
上篇博客我们介绍了View的测量过程,这只是View显示过程的第一步,第二步就是layout了,这个我们一般译作布局,其实就是在View测量完成之后根据View的大小,将其一个一个摆放在ViewGro ...
- 命令行界面下用户和组管理之userdel的使用
userdel - delete a user account and related files 在Linux中,userdel命令的功能是删除系统中的用户及相关的文件 语法 userdel ...