本文首發於cartoon的博客
轉載請註明出處:cartoonyu.github.io/cartoon-blo…java
近段時間在寫leetcode的Lemonade Change時候,發現了for循環與forEach循環的耗時是不一致的,在提交記錄上面差了一倍...... 日常開發絕大部分業務邏輯的實現都須要遍歷機制的幫忙,雖然說也有注意到各數據結構操做的性能比較,可是忽視了遍歷機制性能的差別。本來前兩天就開始動手寫,拖延症......git
現階段我所知道JAVA遍歷機制有三種github
for循環web
forEach循環算法
Iterator循環數組
JAVA數據結構千千萬,可是大部分都是對基礎數據結構的封裝,比較HashMap依賴於Node數組,LinkedList底層是鏈表,ArrayList對數組的再封裝......扯遠了bash
總結來講,JAVA的基礎數據結構,我以爲有兩種數據結構
若是是加上Hash(Hash的操做與數組以及鏈表不太一致),就是三種dom
由於日常開發大部分都優先選擇包裝後的數據結構,因此下面我會使用post
這三種數據結構在遍歷機制不一樣的時候時間的差別
可能有人對我爲何不對比HashMap呢,由於JAVA設計中,是先實現了Map,再實現Set。若是你有閱讀過源碼就會發現:每一個Set子類的實現中,都有一個序列化後的Map對應屬性實現,而由於Hash的查找時間複雜度爲O(1),得出key後查找value的時間大體是一致的,因此我不對比HashMap。
題外話
我在閱讀《瘋狂JAVA》讀到:JAVA的設計者將Map的內部entry數組中的value設爲null進而實現了Set。由於我是以源碼以及官方文檔爲準,具體我不清楚正確與否,可是由於Hash中的key互不相同,Set中元素也互不相同,因此我認爲這個觀點是正確的。
爲了測試的公平性,我會採起如下的限定
注:時間開銷受本地環境的影響,可能測量值會出現變化,可是整體上比例是正確的
代碼
public class TextArray {
private static Random random;
private static List<Integer> list1;
private static List<Integer> list2;
private static List<Integer> list3;
public static void execute(){
random=new Random();
initArray();
testForWith10Object();
testForEachWith10Object();
testIteratorWith10Object();
testForWith100Object();
testForEachWith100Object();
testIteratorWith100Object();
testForWith1000Object();
testForEachWith1000Object();
testIteratorWith1000Object();
}
private static void testForWith10Object(){
printFor(list1);
}
private static void testForWith100Object(){
printFor(list2);
}
private static void testForWith1000Object(){
printFor(list3);
}
private static void testForEachWith10Object(){
printForeach(list1);
}
private static void testForEachWith100Object(){
printForeach(list2);
}
private static void testForEachWith1000Object(){
printForeach(list3);
}
private static void testIteratorWith10Object() {
printIterator(list1);
}
private static void testIteratorWith100Object() {
printIterator(list2);
}
private static void testIteratorWith1000Object() {
printIterator(list3);
}
private static void printFor(List<Integer> list){
System.out.println();
System.out.print("data:");
long start=System.currentTimeMillis();
for(int i=0,length=list.size();i<length;i++){
System.out.print(list.get(i)+" ");
}
System.out.println();
long end=System.currentTimeMillis();
System.out.println("for for "+list.size()+":"+(end-start)+"ms");
}
private static void printForeach(List<Integer> list){
System.out.println();
System.out.print("data:");
long start=System.currentTimeMillis();
for(int temp:list){
System.out.print(temp+" ");
}
System.out.println();
long end=System.currentTimeMillis();
System.out.println("foreach for "+list.size()+":"+(end-start)+"ms");
}
private static void printIterator(List<Integer> list){
System.out.println();
System.out.print("data:");
Iterator<Integer> it=list.iterator();
long start=System.currentTimeMillis();
while(it.hasNext()){
System.out.print(it.next()+" ");
}
System.out.println();
long end=System.currentTimeMillis();
System.out.println("iterator for "+list.size()+":"+(end-start)+"ms");
}
private static void initArray(){
list1=new ArrayList<>();
list2=new ArrayList<>();
list3=new ArrayList<>();
for(int i=0;i<10;i++){
list1.add(random.nextInt());
}
for(int i=0;i<100;i++){
list2.add(random.nextInt());
}
for(int i=0;i<1000;i++){
list3.add(random.nextInt());
}
}
}
複製代碼
輸出(忽略對元素的輸出)
for for 10:1ms
foreach for 10:0ms
iterator for 10:2ms
for for 100:5ms
foreach for 100:4ms
iterator for 100:12ms
for for 1000:33ms
foreach for 1000:7ms
iterator for 1000:16ms
複製代碼
10 | 100 | 1000 |
---|---|---|
for | 1ms | 5ms |
forEach | 0ms | 4ms |
Iterator | 2ms | 12ms |
結論
for的性能最不穩定,foreach次之,Iterator最好
使用建議
在數據量不明確的狀況下(可能1w,10w或其餘),建議使用Iterator進行遍歷
在數據量明確且量級小的時候,優先使用foreach
須要使用索引時,使用遞增變量的開銷比for的要小
代碼
public class TextLinkedList {
private static Random random;
private static List<Integer> list1;
private static List<Integer> list2;
private static List<Integer> list3;
public static void execute(){
random=new Random();
initList();
testForWith10Object();
testForEachWith10Object();
testIteratorWith10Object();
testForWith100Object();
testForEachWith100Object();
testIteratorWith100Object();
testForWith1000Object();
testForEachWith1000Object();
testIteratorWith1000Object();
}
private static void testForWith10Object() {
printFor(list1);
}
private static void testForEachWith10Object() {
printForeach(list1);
}
private static void testIteratorWith10Object() {
printIterator(list1);
}
private static void testForWith100Object() {
printFor(list2);
}
private static void testForEachWith100Object() {
printForeach(list2);
}
private static void testIteratorWith100Object() {
printIterator(list2);
}
private static void testForWith1000Object() {
printFor(list3);
}
private static void testForEachWith1000Object() {
printForeach(list3);
}
private static void testIteratorWith1000Object() {
printIterator(list3);
}
private static void printFor(List<Integer> list){
System.out.println();
System.out.print("data:");
long start=System.currentTimeMillis();
for(int i=0,size=list.size();i<size;i++){
System.out.print(list.get(i));
}
System.out.println();
long end=System.currentTimeMillis();
System.out.println("for for "+list.size()+":"+(end-start)+"ms");
}
private static void printForeach(List<Integer> list){
System.out.println();
System.out.print("data:");
long start=System.currentTimeMillis();
for(int temp:list){
System.out.print(temp+" ");
}
System.out.println();
long end=System.currentTimeMillis();
System.out.println("foreach for "+list.size()+":"+(end-start)+"ms");
}
private static void printIterator(List<Integer> list){
System.out.println();
System.out.print("data:");
Iterator<Integer> it=list.iterator();
long start=System.currentTimeMillis();
while(it.hasNext()){
System.out.print(it.next()+" ");
}
System.out.println();
long end=System.currentTimeMillis();
System.out.println("iterator for "+list.size()+":"+(end-start)+"ms");
}
private static void initList() {
list1=new LinkedList<>();
list2=new LinkedList<>();
list3=new LinkedList<>();
for(int i=0;i<10;i++){
list1.add(random.nextInt());
}
for(int i=0;i<100;i++){
list2.add(random.nextInt());
}
for(int i=0;i<1000;i++){
list3.add(random.nextInt());
}
}
}
複製代碼
輸出(忽略對元素的輸出)
for for 10:0ms
foreach for 10:1ms
iterator for 10:0ms
for for 100:1ms
foreach for 100:0ms
iterator for 100:3ms
for for 1000:23ms
foreach for 1000:25ms
iterator for 1000:4ms
複製代碼
10 | 100 | 1000 |
---|---|---|
for | 0ms | 1ms |
forEach | 1ms | 0ms |
Iterator | 0ms | 3ms |
結論
foreach的性能最不穩定,for次之,Iterator最好
使用建議
儘可能使用Iterator進行遍歷
須要使用索引時,使用遞增變量的開銷比for的要小
注:因Hash遍歷算法與其餘類型不一致,因此取消了for循環的比較
代碼
public class TextHash {
private static Random random;
private static Set<Integer> set1;
private static Set<Integer> set2;
private static Set<Integer> set3;
public static void execute(){
random=new Random();
initHash();
testIteratorWith10Object();
testForEachWith10Object();
testIteratorWith100Object();
testForEachWith100Object();
testIteratorWith1000Object();
testForEachWith1000Object();
}
private static void testIteratorWith10Object() {
printIterator(set1);
}
private static void testForEachWith10Object() {
printForeach(set1);
}
private static void testIteratorWith100Object() {
printIterator(set2);
}
private static void testForEachWith100Object() {
printForeach(set2);
}
private static void testIteratorWith1000Object() {
printIterator(set3);
}
private static void testForEachWith1000Object() {
printForeach(set3);
}
private static void initHash() {
set1=new HashSet<>();
set2=new HashSet<>();
set3=new HashSet<>();
for(int i=0;i<10;i++){
set1.add(random.nextInt());
}
for(int i=0;i<100;i++){
set2.add(random.nextInt());
}
for(int i=0;i<1000;i++){
set3.add(random.nextInt());
}
}
private static void printIterator(Set<Integer> data){
System.out.println();
System.out.print("data:");
long start=System.currentTimeMillis();
Iterator<Integer> it=data.iterator();
while (it.hasNext()){
System.out.print(it.next()+" ");
}
System.out.println();
long end=System.currentTimeMillis();
System.out.println("iterator for "+data.size()+":"+(end-start)+"ms");
}
private static void printForeach(Set<Integer> data){
System.out.println();
System.out.print("data:");
long start=System.currentTimeMillis();
for(int temp:data){
System.out.print(temp+" ");
}
System.out.println();
long end=System.currentTimeMillis();
System.out.println("foreach for "+data.size()+":"+(end-start)+"ms");
}
}
複製代碼
輸出(忽略對元素的輸出)
iterator for 10:0ms
foreach for 10:0ms
iterator for 100:6ms
foreach for 100:0ms
iterator for 1000:30ms
foreach for 1000:9ms
複製代碼
10 | 100 | 1000 |
---|---|---|
foreach | 0ms | 0ms |
Iterator | 0ms | 6ms |
結論
foreach性能遙遙領先於Iterator
使用建議
之後就選foreach了,性能好,寫起來也方便。
以上就是我對常見數據結構遍歷機制的一點比較,雖然只是很初步,可是從中我也學到了不少東西,但願大家也有所收穫。
若是你喜歡本文章,能夠收藏閱讀,若是你對個人其餘文章感興趣,歡迎到個人博客查看。