1. 程式人生 > >java中常見集合類的遍歷

java中常見集合類的遍歷

一、前言

  我們經常在工作當中使用到集合,java當中的集合類較多,且自帶有豐富方法可對集合中的元素進行靈活操作,我們在使用時不必考慮資料結構和演算法實現細節,只需建立集合物件直接使用即可,這給我們帶來了極大的便利。本文對日常工作中常用的集合遍歷問題進行簡單總結。

二、java集合類圖

  為了便於理解,這裡貼一張 java 集合類圖。(來自網路)
  這裡寫圖片描述

三、java常用集合遍歷

  java中的集合類眾多,但我們常用的是ArrayList、HashMap、HashSet這幾個類,本文就一這3個類分別作為 List、Map、Set 這3類集合遍歷的例子。
  1、ArrayList遍歷


例1:

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * @desc 集合遍歷測試
 *
 * @author xiaojiang
 *
 */
public class CollectionTest {
    public static void main(String[] args) {
        //初始化測試資料
        List<String> dataList = new ArrayList<String>();
        for
(int i=0;i<10000000;i++){ dataList.add("data" + i); } //1、for迴圈遍歷 int size = dataList.size(); long start_1 = System.currentTimeMillis(); for(int i=0; i<size ;i++){ String temp = dataList.get(i); //相關業務操作 } long end_1 = System.currentTimeMillis(); //2、foreach遍歷
long start_2 = System.currentTimeMillis(); for(String s:dataList){ String temp = s; //相關業務操作 } long end_2 = System.currentTimeMillis(); //3、Iterator遍歷 long start_3 = System.currentTimeMillis(); Iterator<String> iterator = dataList.iterator(); while(iterator.hasNext()){ String temp = iterator.next(); //相關業務操作 } long end_3 = System.currentTimeMillis(); //輸出 System.out.println("for迴圈遍歷,耗時(ms)->" + (end_1 - start_1)); System.out.println("foreach遍歷,耗時(ms)->" + (end_2 - start_2)); System.out.println("Iterator遍歷,耗時(ms)->" + (end_3 - start_3)); } }

輸出:

//1for迴圈遍歷,耗時(ms)->48
foreach遍歷,耗時(ms)->47
Iterator遍歷,耗時(ms)->50
//第2for迴圈遍歷,耗時(ms)->55
foreach遍歷,耗時(ms)->56
Iterator遍歷,耗時(ms)->55
//第3for迴圈遍歷,耗時(ms)->52
foreach遍歷,耗時(ms)->57
Iterator遍歷,耗時(ms)->54

  從上面結果可以初步發現:for迴圈方式使用方便一些,foreach方式的程式碼更簡潔一些,Iterator方式的方法更靈活一些(如:其可以在集合遍歷過程中 remove 元素);效能方面,對於 ArrayList 的遍歷,3種方式的效率差不多,其中“for迴圈”遍歷的方式略快一些。(注意:LinkedList 切勿用“for迴圈”方式,因為LinkedList資料結構決定了其獲取指定節點的元素相對較慢。

  2、HashMap遍歷
例2:

/**
 * @desc 集合遍歷測試
 *
 * @author xiaojiang
 *
 */
public class CollectionTest {
    public static void main(String[] args) throws InterruptedException {
        // 初始化測試資料
        Map<String,Object> map = new HashMap<String, Object>();
        for (int i = 0; i < 10000000; i++) {
            map.put("key" + i, i);
        }

        //1、map.Entry()遍歷
        long start_1 = System.currentTimeMillis();
        for(Map.Entry<String, Object> entry : map.entrySet()){
            //相關業務操作
            //System.out.println(entry.getKey() + ":" + entry.getValue());
        }
        long end_1 = System.currentTimeMillis();

        //2、Iterator遍歷
        long start_2 = System.currentTimeMillis();
        Iterator<Entry<String, Object>> iterator = map.entrySet().iterator();  
        while (iterator.hasNext()) {  
           Map.Entry<String, Object> entry = iterator.next();  
           //相關業務操作
           //System.out.println(entry.getKey() + ":" + entry.getValue()); 
        }  
        long end_2 = System.currentTimeMillis();

        //3、map.keySet()遍歷
        long start_3 = System.currentTimeMillis();
        for(String key:map.keySet()){
            //相關業務操作
            //System.out.println(key + ":" + map.get(key));
        }
        long end_3 = System.currentTimeMillis();

        //4、map.values()遍歷獲取 value
        long start_4 = System.currentTimeMillis();
        for (Object value : map.values()) {  
            //相關業務操作
           //System.out.println(":" + value);  
        }  
        long end_4 = System.currentTimeMillis();

        //輸出
        System.out.println("Map.Entry遍歷,耗時(ms)->" + (end_1 - start_1));
        System.out.println("Iterator遍歷,耗時(ms)->" + (end_2 - start_2));
        System.out.println("map.keySet()遍歷,耗時(ms)->" + (end_3 - start_3));
        System.out.println("map.values()遍歷獲取 value,耗時(ms)->" + (end_4 - start_4));
    }
}

輸出:  

//1次
Map.Entry遍歷,耗時(ms)->195
Iterator遍歷,耗時(ms)->203
map.keySet()遍歷,耗時(ms)->236
map.values()遍歷獲取 value,耗時(ms)->237
//第2次
Map.Entry遍歷,耗時(ms)->187
Iterator遍歷,耗時(ms)->192
map.keySet()遍歷,耗時(ms)->225
map.values()遍歷獲取 value,耗時(ms)->215
//第3次
Map.Entry遍歷,耗時(ms)->182
Iterator遍歷,耗時(ms)->199
map.keySet()遍歷,耗時(ms)->214
map.values()遍歷獲取 value,耗時(ms)->213

  從上面結果可以初步發現:Map.Entry方式遍歷效率相對最高;Iterator方式的優點還是靈活;map.keySet()、map.values()這2種方式可分別對Map中所有的 key、value進行遍歷,其中map.keySet()方式可通過遍歷獲取的 key 再次取值獲取相應 value(這種方式獲取key、value的效率較低),這2種方式程式碼更簡潔些,適用於僅需遍歷所有 key 或所有 value 的場景。

  3、HashSet遍歷
例3:

import java.util.HashSet;
import java.util.Iterator;
/**
 * @desc 集合遍歷測試
 *
 * @author xiaojiang
 *
 */
public class CollectionTest {
    public static void main(String[] args) throws InterruptedException {
        // 初始化測試資料
        HashSet<String> set = new HashSet<String>();
        for (int i = 0; i < 10000000; i++) {
            set.add("data" + i);
        }

        // 1、foreach遍歷
        long start_1 = System.currentTimeMillis();
        for (String s : set) {
            // 相關業務操作
            String temp = s;
        }
        long end_1 = System.currentTimeMillis();

        // 2、Iterator遍歷
        long start_2 = System.currentTimeMillis();
        Iterator<String> iterator = set.iterator();
        while (iterator.hasNext()) {
            // 相關業務操作
            String temp = iterator.next();
        }
        long end_2 = System.currentTimeMillis();

        // 3、先轉換成陣列,再遍歷
        long start_3 = System.currentTimeMillis();
        Object array[] = set.toArray();
        for (int i = 0; i < array.length; i++) {
            // 相關業務操作
            String temp = (String)array[i];
        }
        long end_3 = System.currentTimeMillis();

        // 輸出
        System.out.println("foreach遍歷,耗時(ms)->" + (end_1 - start_1));
        System.out.println("Iterator遍歷,耗時(ms)->" + (end_2 - start_2));
        System.out.println("先轉換成陣列,再遍歷,耗時(ms)->" + (end_3 - start_3));
    }
}

輸出: 

//1次
foreach遍歷,耗時(ms)->171
Iterator遍歷,耗時(ms)->201
先轉換成陣列,再遍歷,耗時(ms)->279  
//第2次
foreach遍歷,耗時(ms)->191
Iterator遍歷,耗時(ms)->194
先轉換成陣列,再遍歷,耗時(ms)->284
//第3次
foreach遍歷,耗時(ms)->197
Iterator遍歷,耗時(ms)->194
先轉換成陣列,再遍歷,耗時(ms)->287

  由以上可知 HashSet 的的遍歷和 ArrayList 的遍歷方法相近(ArrayList 其實也可以先轉成陣列再遍歷),均可通過 foreach 遍歷、Iterator遍歷,但 HashSet 不可通過“for迴圈”的方式遍歷,因為其沒有根據索引號獲取元素的方法。

四、總結

1、遍歷集合前需檢查集合是否為空,若為空會拋 java.lang.NullPointerException 異常。
2、在集合遍歷時,經常會涉及的一個問題是執行緒安全問題,如:
  a、Vector和ArrayList相比,兩者功能類似,都繼承了AbstractList類,但Vector效率沒有ArrayList高,所以通常我們都使用ArrayList,而感覺Vector較陌生,不過,Vector是執行緒安全的而ArrayList是非執行緒安全的,所以,當多執行緒需要執行緒安全時,我們可以用Vector。
  b、Hashtable 和 HashMap 也是功能相似,但Hashtable 是執行緒安全的而HashMap是非執行緒安全的,所以當需要執行緒安全時,可以用Hashtable 。
  當然,ArrayList、HashMap也是可以通過Collections類中的Collections.synchronizedList(list)、Collections.synchronizedMap(m) 方法操作來達到執行緒安全的目的。
3、List、Map、Set等集合類的功能豐富,使用也常見,且靈活運用可給開發帶來較大的便利。
4、雖然理論上集合可以儲存大量資料(只要記憶體夠),但是也需儘量避免集合中的資料量過大,以避免JVM發生長時間GC的問題。