Java不同數據存儲類型使用不同遍歷方法效率研究
GitHub代碼倉庫
數據存儲類型
- ArrayList
- HashSet
- HashMap
- LinkedList
遍歷方法
- 傳統遍歷方法
for(int i=0;i<list.size();i++) {
String str = list.get(i);
...
}
- 內置迭代器
for (String str : list) {
...
}
- 顯式迭代器
Iterator<String> it = list.iterator();
while(it.hasNext()) {
String str = it.next();
...
}
測試代碼模板
- 使用大小爲的數組,遍歷邊,平均遍歷速度定義爲
private static ArrayList<String> list = new ArrayList<>();
private final static int N = 1000000, M = 1000;
private final static String STR = "abcdefg";
- 首先建立一個固定數組,供給3個遍歷方法使用
@BeforeClass
public static void CreateList() {
for (int i = 0; i < N; i++) {
list.add(STR);
}
}
使用JUnit測試單元記錄時間
- 傳統遍歷方法For
@Test
public void FOR() {
for (int k = 0; k < M; k++) {
for (int i = 0; i < list.size(); i++) {
String str = list.get(i);
}
}
}
- 內置迭代器
@Test
public void Inner_Iteration() {
for (int k = 0; k < M; k++) {
for (String str : list) {
String s = str;
}
}
}
- 顯式迭代器
@Test
public void Explicit_Iteration() {
for (int k = 0; k < M; k++) {
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String str = it.next();
}
}
}
根據不同的存儲類型進行更改
eg. HashMap 要設置key和value
Python數據可視化
import pandas as pd
import numpy as np
from matplotlib import pyplot as plt
import seaborn as sns
%matplotlib inline
- 以ArrayList代碼爲例
- 柱狀圖
f, ax= plt.subplots()
ArrayList = pd.read_csv("../csv/ArrayList.csv")
sns.barplot(data=ArrayList)
ax.set_title("ArrayList")
- 折線圖
f, ax= plt.subplots()
sns.lineplot(data=ArrayList)
ax.set_title("ArrayList")
plt.ylim(0.5, 1.0)
ArrayList
- 整體來說,在ArrayList中for的遍歷速度最快,內置迭代器和顯式迭代器相當
- 並且ArrayList迭代非常穩定,尤其是for
Explicit_Iteration | FOR | Inner_Iteration |
---|---|---|
0.775 | 0.585 | 0.757 |
0.785 | 0.577 | 0.748 |
0.783 | 0.581 | 0.769 |
0.775 | 0.583 | 0.77 |
0.778 | 0.602 | 0.788 |
0.784 | 0.586 | 0.767 |
0.775 | 0.581 | 0.807 |
0.814 | 0.587 | 0.766 |
0.786 | 0.604 | 0.839 |
0.815 | 0.593 | 0.775 |
HashMap
- 在HashMap中不能使用For,內置迭代器略優
Explicit_Iteration | Inner_Iteration |
---|---|
0.608 | 0.507 |
0.622 | 0.613 |
0.585 | 0.485 |
0.587 | 0.533 |
0.639 | 0.526 |
0.593 | 0.555 |
0.534 | 0.431 |
0.581 | 0.504 |
0.598 | 0.481 |
0.64 | 0.504 |
HashSet
- 在HashSet中同樣不能使用for, 基本相同,內置迭代器較不穩定
Explicit_Iteration | Inner_Iteration |
---|---|
2.5 | 2.517 |
2.532 | 2.477 |
2.684 | 2.523 |
2.697 | 3.303 |
2.631 | 2.515 |
2.956 | 3.016 |
3.281 | 3.051 |
3.074 | 2.95 |
3.206 | 2.935 |
2.89 | 2.887 |
LinkedList
- 注:在鏈表中可以使用for,但是根據一定的測試,隨着數據規模增加,運行時間呈指數型增長,與另兩種遍歷方法不在一個數量級上,所以不加入統計。
- 在LinkedList中,內置迭代器和顯式迭代器效率相當,多個單次試驗觀察來說顯式迭代器略優
Explicit_Iteration | Inner_Iteration |
---|---|
1.865 | 2.101 |
1.883 | 1.929 |
1.9 | 1.941 |
1.843 | 1.903 |
1.89 | 1.897 |
1.835 | 1.921 |
1.841 | 1.846 |
1.914 | 1.902 |
1.872 | 1.859 |
1.816 | 1.827 |
數據處理和數據清洗
- 將4中存儲類型的數據整合起來
- 將數據統一到同樣的數據尺度
Base = 0
ArrayList /= 10**(Base-8)
ArrayList['Type'] = 'ArrayList' # 8
HashMap /= 10**(Base-7)
HashMap['Type'] = 'HashMap' # 7
HashSet /= 10**(Base-8)
HashSet['Type'] = 'HashSet' # 8
LinkedList /= 10**(Base-8)
LinkedList['Type'] = 'LinkedList' # 8
- 由於不同數據類型效率差異較大
- 作者選擇通過取對數的方法
- 然後取相反數(時間越少,效率越高)
- 這使數據數量級接近,能容易可視化
data = pd.concat([ArrayList, HashMap, HashSet, LinkedList], ignore_index=True).drop(['FOR'], axis=1)
data[['Explicit_Iteration', 'Inner_Iteration']] = np.log10(data[['Explicit_Iteration', 'Inner_Iteration']])
整體可視化對比
- 將內置迭代器、顯式迭代器分別處理後數據對比
- 下圖的數據表示效率相對值的數量級
f, ax= plt.subplots()
sns.barplot(x='Type', y='Inner_Iteration', data=data)
ax.set_title("Inner_Iteration")
f, ax= plt.subplots()
sns.barplot(x='Type', y='Explicit_Iteration', data=data)
ax.set_title("Explicit_Iteration")
分析
本質:數組、集合、字典、鏈表,4種數據結構的差異在遍歷方式上的體現
ArrayList
- ArrayList本質上是一個動態數組,數組對於大量隨機訪問有着高效的響應速度
- 迭代器在ArrayList這種不依賴
__iter__
、__next__
方法的對象,在使用迭代器時的訪存速度遠不如有序訪存的FOR
- 由於數組線性存儲,導致ArrayList增加和刪除操作效率較低
- 因此ArrayList適用於不定長、不頻繁增刪的數據存儲
HashMap
- Java種的Map主要分爲HashMap和TreeMap,屬於非Collection接口
- Map需要有鍵key和值value,內部元素無序,因此無法用For訪問
- 使用迭代器時,由於鍵值的唯一性,單個元素查找速度似乎較快
- 但迭代對象時鍵值的集合,
keySet()
的迭代需要佔用時間 - 而且鍵值是無序的,一定程度上,不滿足良好局部性的要求
HashSet
- HashSet繼承了Collection種的Set
- HashSet調用了
HashMap.put()
方法,將值直接作爲鍵 - 所以HashSet在訪問時一定優於HashMap,因爲Set不需要對Keys的迭代
- 事實證明,HashSet確實遠優於HashMap,但從實用性角度來說卻不如HashMap
LinkedList
- LinkedList本質上是一個雙向鏈表
- 鏈表的特點就是容易增加和刪除,但隨機訪問單個元素效率很低
總結
- 不同的4種數據存儲類型中3種迭代方式效率不同
- 內置迭代器和顯式迭代器效率相當,for在ArrayList中效率較高、LinkedList很差
- HashSet的迭代效率較高,HashMap迭代效率較低
- 單從迭代器迭代速度來說:HashSet > LinkedList > ArrayList > HashMap
- 總體評價:
- ArrayList:少增刪,求穩定
- HashMap:字典功能,效率低
- HashSet:大數據非數字隨機元素查找極快
- LinkedList:增刪高速,嚴禁用for