一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|編程技術|正則表達式|

服務器之家 - 編程語言 - JAVA教程 - Java中ArrayList和LinkedList的遍歷與性能分析

Java中ArrayList和LinkedList的遍歷與性能分析

2020-07-09 11:23daisy JAVA教程

這篇文章主要給大家介紹了ArrayList和LinkedList這兩種list的五種循環遍歷方式,各種方式的性能測試對比,根據ArrayList和LinkedList的源碼實現分析性能結果,總結結論。相信對大家的理解和學習具有一定的參考價值,有需要的朋友們下

前言

通過本文你可以了解List的五種遍歷方式及各自性能和foreach及Iterator的實現,加深對ArrayListLinkedList實現的了解。下面來一起看看吧。

一、List的五種遍歷方式

1、for each循環

?
1
2
3
4
List<Integer> list = new ArrayList<Integer>();
for (Integer j : list) {
 // use j
}

2、顯示調用集合迭代器

?
1
2
3
4
List<Integer> list = new ArrayList<Integer>();
for (Iterator<Integer> iterator = list.iterator(); iterator.hasNext();) {
 iterator.next();
}

?
1
2
3
4
5
List<Integer> list = new ArrayList<Integer>();
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()) {
 iterator.next();
}

3、下標遞增循環,終止條件為每次調用size()函數比較判斷

?
1
2
3
4
List<Integer> list = new ArrayList<Integer>();
for (int j = 0; j < list.size(); j++) {
 list.get(j);
}

4、下標遞增循環,終止條件為和等于size()的臨時變量比較判斷

?
1
2
3
4
5
List<Integer> list = new ArrayList<Integer>();
int size = list.size();
for (int j = 0; j < size; j++) {
 list.get(j);
}

5、下標遞減循環

?
1
2
3
4
List<Integer> list = new ArrayList<Integer>();
for (int j = list.size() - 1; j >= 0; j--) {
 list.get(j);
}

List五種遍歷方式的性能測試及對比

以下是性能測試代碼,會輸出不同數量級大小的ArrayList和LinkedList各種遍歷方式所花費的時間。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package cn.trinea.java.test;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
/**
 * JavaLoopTest
 *
 * @author www.trinea.cn 2013-10-28
 */
public class JavaLoopTest {
 public static void main(String[] args) {
  System.out.print("compare loop performance of ArrayList");
  loopListCompare(getArrayLists(10000, 100000, 1000000, 9000000));
  System.out.print("\r\n\r\ncompare loop performance of LinkedList");
  loopListCompare(getLinkedLists(100, 1000, 10000, 100000));
 }
 public static List<Integer>[] getArrayLists(int... sizeArray) {
  List<Integer>[] listArray = new ArrayList[sizeArray.length];
  for (int i = 0; i < listArray.length; i++) {
   int size = sizeArray[i];
   List<Integer> list = new ArrayList<Integer>();
   for (int j = 0; j < size; j++) {
    list.add(j);
   }
   listArray[i] = list;
  }
  return listArray;
 }
 public static List<Integer>[] getLinkedLists(int... sizeArray) {
  List<Integer>[] listArray = new LinkedList[sizeArray.length];
  for (int i = 0; i < listArray.length; i++) {
   int size = sizeArray[i];
   List<Integer> list = new LinkedList<Integer>();
   for (int j = 0; j < size; j++) {
    list.add(j);
   }
   listArray[i] = list;
  }
  return listArray;
 }
 public static void loopListCompare(List<Integer>... listArray) {
  printHeader(listArray);
  long startTime, endTime;
  // Type 1
  for (int i = 0; i < listArray.length; i++) {
   List<Integer> list = listArray[i];
   startTime = Calendar.getInstance().getTimeInMillis();
   for (Integer j : list) {
    // use j
   }
   endTime = Calendar.getInstance().getTimeInMillis();
   printCostTime(i, listArray.length, "for each", endTime - startTime);
  }
  // Type 2
  for (int i = 0; i < listArray.length; i++) {
   List<Integer> list = listArray[i];
   startTime = Calendar.getInstance().getTimeInMillis();
   // Iterator<Integer> iterator = list.iterator();
   // while(iterator.hasNext()) {
   // iterator.next();
   // }
   for (Iterator<Integer> iterator = list.iterator(); iterator.hasNext();) {
    iterator.next();
   }
   endTime = Calendar.getInstance().getTimeInMillis();
   printCostTime(i, listArray.length, "for iterator", endTime - startTime);
  }
  // Type 3
  for (int i = 0; i < listArray.length; i++) {
   List<Integer> list = listArray[i];
   startTime = Calendar.getInstance().getTimeInMillis();
   for (int j = 0; j < list.size(); j++) {
    list.get(j);
   }
   endTime = Calendar.getInstance().getTimeInMillis();
   printCostTime(i, listArray.length, "for list.size()", endTime - startTime);
  }
  // Type 4
  for (int i = 0; i < listArray.length; i++) {
   List<Integer> list = listArray[i];
   startTime = Calendar.getInstance().getTimeInMillis();
   int size = list.size();
   for (int j = 0; j < size; j++) {
    list.get(j);
   }
   endTime = Calendar.getInstance().getTimeInMillis();
   printCostTime(i, listArray.length, "for size = list.size()", endTime - startTime);
  }
  // Type 5
  for (int i = 0; i < listArray.length; i++) {
   List<Integer> list = listArray[i];
   startTime = Calendar.getInstance().getTimeInMillis();
   for (int j = list.size() - 1; j >= 0; j--) {
    list.get(j);
   }
   endTime = Calendar.getInstance().getTimeInMillis();
   printCostTime(i, listArray.length, "for j--", endTime - startTime);
  }
 }
 static int     FIRST_COLUMN_LENGTH = 23, OTHER_COLUMN_LENGTH = 12, TOTAL_COLUMN_LENGTH = 71;
 static final DecimalFormat COMMA_FORMAT  = new DecimalFormat("#,###");
 public static void printHeader(List<Integer>... listArray) {
  printRowDivider();
  for (int i = 0; i < listArray.length; i++) {
   if (i == 0) {
    StringBuilder sb = new StringBuilder().append("list size");
    while (sb.length() < FIRST_COLUMN_LENGTH) {
     sb.append(" ");
    }
    System.out.print(sb);
   }
   StringBuilder sb = new StringBuilder().append("| ").append(COMMA_FORMAT.format(listArray[i].size()));
   while (sb.length() < OTHER_COLUMN_LENGTH) {
    sb.append(" ");
   }
   System.out.print(sb);
  }
  TOTAL_COLUMN_LENGTH = FIRST_COLUMN_LENGTH + OTHER_COLUMN_LENGTH * listArray.length;
  printRowDivider();
 }
 public static void printRowDivider() {
  System.out.println();
  StringBuilder sb = new StringBuilder();
  while (sb.length() < TOTAL_COLUMN_LENGTH) {
   sb.append("-");
  }
  System.out.println(sb);
 }
 public static void printCostTime(int i, int size, String caseName, long costTime) {
  if (i == 0) {
   StringBuilder sb = new StringBuilder().append(caseName);
   while (sb.length() < FIRST_COLUMN_LENGTH) {
    sb.append(" ");
   }
   System.out.print(sb);
  }
  StringBuilder sb = new StringBuilder().append("| ").append(costTime).append(" ms");
  while (sb.length() < OTHER_COLUMN_LENGTH) {
   sb.append(" ");
  }
  System.out.print(sb);
  if (i == size - 1) {
   printRowDivider();
  }
 }
}

PS:如果運行報異常in thread “main” java.lang.OutOfMemoryError: Java heap space,請將main函數里面list size的大小減小。

其中getArrayLists函數會返回不同size的ArrayList,getLinkedLists函數會返回不同size的LinkedList。

loopListCompare函數會分別用上面的遍歷方式1-5去遍歷每一個list數組(包含不同大小list)中的list。

print開頭函數為輸出輔助函數。

測試環境為Windows7 32位系統 3.2G雙核CPU 4G內存,Java 7,Eclipse -Xms512m -Xmx512m

最終測試結果如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
compare loop performance of ArrayList
-----------------------------------------------------------------------
list size    | 10,000 | 100,000 | 1,000,000 | 10,000,000
-----------------------------------------------------------------------
for each    | 1 ms  | 3 ms  | 14 ms  | 152 ms
-----------------------------------------------------------------------
for iterator   | 0 ms  | 1 ms  | 12 ms  | 114 ms
-----------------------------------------------------------------------
for list.size()  | 1 ms  | 1 ms  | 13 ms  | 128 ms
-----------------------------------------------------------------------
for size = list.size() | 0 ms  | 0 ms  | 6 ms  | 62 ms 
-----------------------------------------------------------------------
for j--    | 0 ms  | 1 ms  | 6 ms  | 63 ms 
-----------------------------------------------------------------------
 
compare loop performance of LinkedList
-----------------------------------------------------------------------
list size    | 100  | 1,000  | 10,000 | 100,000
-----------------------------------------------------------------------
for each    | 0 ms  | 1 ms  | 1 ms  | 2 ms 
-----------------------------------------------------------------------
for iterator   | 0 ms  | 0 ms  | 0 ms  | 2 ms 
-----------------------------------------------------------------------
for list.size()  | 0 ms  | 1 ms  | 73 ms  | 7972 ms
-----------------------------------------------------------------------
for size = list.size() | 0 ms  | 0 ms  | 67 ms  | 8216 ms
-----------------------------------------------------------------------
for j--    | 0 ms  | 1 ms  | 67 ms  | 8277 ms
-----------------------------------------------------------------------

第一張表為ArrayList對比結果,第二張表為LinkedList對比結果。

表橫向為同一遍歷方式不同大小list遍歷的時間消耗,縱向為同一list不同遍歷方式遍歷的時間消耗。

PS:由于首次遍歷List會稍微多耗時一點,for each的結果稍微有點偏差,將測試代碼中的幾個Type順序調換會發現,for each耗時和for iterator接近。

遍歷方式性能測試結果分析

1、foreach介紹

foreach是Java SE5.0引入的功能很強的循環結構,for (Integer j : list)應讀作for each int in list

for (Integer j : list)實現幾乎等價于

?
1
2
3
4
Iterator<Integer> iterator = list.iterator();
while(iterator.hasNext()) {
 Integer j = iterator.next();
}

foreach代碼書寫簡單,不必關心下標初始值和終止值及越界等,所以不易出錯

2、ArrayList遍歷方式結果分析

a. 在ArrayList大小為十萬之前,五種遍歷方式時間消耗幾乎一樣

b. 在十萬以后,第四、五種遍歷方式快于前三種,get方式優于Iterator方式,并且

?
1
2
3
4
int size = list.size();
for (int j = 0; j < size; j++) {
 list.get(j);
}

用臨時變量size取代list.size()性能更優。我們看看ArrayList中迭代器Iteratorget方法的實現

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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 E get(int index) {
 rangeCheck(index);
 
 return elementData(index);
}

從中可以看出getIteratornext函數同樣通過直接定位數據獲取元素,只是多了幾個判斷而已。

c. 從上可以看出即便在千萬大小的ArrayList中,幾種遍歷方式相差也不過50ms左右,且在常用的十萬左右時間幾乎相等,考慮foreach的優點,我們大可選用foreach這種簡便方式進行遍歷。

3、LinkedList遍歷方式結果分析

a. 在LinkedList大小接近一萬時,get方式和Iterator方式就已經差了差不多兩個數量級,十萬時Iterator方式性能已經遠勝于get方式。

我們看看LinkedList中迭代器和get方法的實現

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
private class ListItr implements ListIterator<E> {
 private Node<E> lastReturned = null;
 private Node<E> next;
 private int nextIndex;
 private int expectedModCount = modCount;
 
 ListItr(int index) {
  // assert isPositionIndex(index);
  next = (index == size) ? null : node(index);
  nextIndex = index;
 }
 
 public boolean hasNext() {
  return nextIndex < size;
 }
 
 public E next() {
  checkForComodification();
  if (!hasNext())
   throw new NoSuchElementException();
 
  lastReturned = next;
  next = next.next;
  nextIndex++;
  return lastReturned.item;
 }
 ……
}
 
public E get(int index) {
 checkElementIndex(index);
 return node(index).item;
}
 
/**
 * Returns the (non-null) Node at the specified element index.
 */
Node<E> node(int index) {
 // assert isElementIndex(index);
 
 if (index < (size >> 1)) {
  Node<E> x = first;
  for (int i = 0; i < index; i++)
   x = x.next;
  return x;
 } else {
  Node<E> x = last;
  for (int i = size - 1; i > index; i--)
   x = x.prev;
  return x;
 }
}

從上面代碼中可以看出LinkedList迭代器的next函數只是通過next指針快速得到下一個元素并返回。而get方法會從頭遍歷直到index下標,查找一個元素時間復雜度為哦O(n),遍歷的時間復雜度就達到了O(n2)。

所以對于LinkedList的遍歷推薦使用foreach,避免使用get方式遍歷。

4、ArrayList和LinkedList遍歷方式結果對比分析

從上面的數量級來看,同樣是foreach循環遍歷,ArrayList和LinkedList時間差不多,可將本例稍作修改加大list size會發現兩者基本在一個數量級上。

ArrayList get函數直接定位獲取的方式時間復雜度為O(1),而LinkedList的get函數時間復雜度為O(n)。

再結合考慮空間消耗的話,建議首選ArrayList。對于個別插入刪除非常多的可以使用LinkedList。

結論總結

通過上面的分析我們基本可以總結下:

  1. 無論ArrayList還是LinkedList,遍歷建議使用foreach,尤其是數據量較大時LinkedList避免使用get遍歷。
  2. List使用首選ArrayList。對于個別插入刪除非常多的可以使用LinkedList。
  3. 可能在遍歷List循環內部需要使用到下標,這時綜合考慮下是使用foreach和自增count還是get方式。

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家學習或者使用Java的時候能有所幫助,如果有疑問大家可以留言交流。

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 五月桃花网婷婷亚洲综合 | 天天视频官网天天视频在线 | 国产精品久久久久久久人人看 | 国内精品一区二区在线观看 | 免费理伦片在线观看全网站 | 色哟哟在线播放 | 国产91精品久久久久久 | 成人在线第一页 | jazz中国女人护士 | 操熟美女又肥又嫩的骚屁股 | 日韩欧美一区二区在线观看 | 香港三级系列在线播放 | 日韩一区二区中文字幕 | bt国产| 波多野结衣之双方调教在线观看 | 久久99国产综合精品AV蜜桃 | 国产欧美一区二区三区久久 | 69av美女| 精品国产成人高清在线 | 欧美日韩高清完整版在线观看免费 | 日韩精品一区二区三区免费视频 | 欧美午夜精品 | 欧美成人免费观看国产 | 国产在线麻豆波多野结衣 | 欧美精品亚洲精品日韩1818 | 男人肌肌捅女人 | 国内精品久久久久久中文字幕 | 果冻传媒天美传媒乌鸦传媒 | 美女被狂揉下部羞羞动漫 | 手机在线免费观看视频 | 欧美精品久久一区二区三区 | 98免费视频 | 精品欧美一区二区三区在线观看 | 日韩视频一区二区三区 | 四虎影院永久网站 | 99视频免费 | 亚洲AV永久无码精品澳门 | 护士videossexo另类 | 亚洲 欧美 日本 国产 高清 | 国产综合欧美日韩视频一区 | 国产精品香蕉夜间视频免费播放 |