一、遍歷方式
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)當我們需要遍歷不同的集合時,我們只需要傳遞集合的iterator(如arr.iterator())看懂了吧,這就是iterator的好處,他不包含任何有關他所遍歷的序列的類型信息,能夠將遍歷序列的操作與序列底層的結構分離。迭代器統一了對容器的訪問方式。這也是接口的解耦的最好體現。
{
while(it.hasNext())
{
system.out.print(it.next()+"");
}
}
4)用法差別
for循環:一般用來處理比較簡單的有序的,可預知大小的集合或數組
foreach:可用於遍歷任何集合或數組,而且操作簡單易懂,他唯一的不好就是需要了解集合內部類型
iterator:是最強大的,它可以隨時修改或者刪除集合內部的元素,並且是在不需要知道元素和集合的類型的情況下進行的(原因可參考第三點:多態差別),當你需要對不同的容器實現同樣的遍歷方式時,迭代器是最好的選擇!
參考: