1. 程式人生 > >Android List、Set和Map的介紹和使用

Android List、Set和Map的介紹和使用

一、前言

Android中常用的資料結構包括List、Set和Map這三大類的集合,其中List和Set屬於Collection。List與Set的區別在於List可以存放重複的資料,但是Set不可以。
Map一般為key-value這樣的對於關係,比如常用的HashMap。
Android中的集合類關係圖

Collection        介面的介面   物件的集合
|-List            子介面      按進入的先後順序進行儲存,可重複
|   |-LinkedList  介面實現類   連結串列,插入刪除,非執行緒安全
|   |-ArrayList   介面實現類   陣列,隨機訪問,執行緒非安全
| |_Vector 介面實現類 陣列,執行緒安全 |_Set 子介面 僅接收一次,並做內部排序 | |-HashSet 介面實現類 HashSet使用Hash演算法來儲存集合中的元素,因此具有良好的存取和查詢效能 | | |-LinkedHashSet 介面實現類 LinkedHashSet集合也是根據元素的hashCode值來決定元素的儲存位置,但是和HashSet不同的是,它同時使用連結串列維護元素的次序,這樣使得元素看起來是以插入的順序儲存的。 |—Queue 子介面 保持一個佇列(先進先出)的順序
|-PriorityQueue Map 介面 把鍵物件和值物件進行關聯的容器,並且值物件可以是另一個Map |-HashMap 介面實現類 根據key算出一個hash值,確定一個存放index,期間需要解決hash衝突。非執行緒安全。 |-LinkedHashMap 繼承自HashMap 相比HashMap,其特點是內部存放資料是有順序的,增加了記住元素插入或者訪問順序的功能。 |-TreeMap 介面的實現類 使用大致與HashMap類似,只是內部實現是根據紅黑樹來實現的。 |HashTable 介面實現類 Hashtable是執行緒安全的,內部實現與HashMap差不多。

二、List、Set和Map的介紹和使用範例

常用的List包括ArrayList、Linked List和Vector,下面將對他們進行一一介紹。
2.1 ArrayList
ArrayList非執行緒安全,底層由陣列實現,它的構造主要從AbstractList實現,主要是判斷下初始元素的容量,ArrayList最大的特點就是提供了Add、Get操作,當然可以通過迭代器來遍歷,對於元素的存在可以通過contains方法判斷。
ArrayList初始化和賦值

//方式一:
  ArrayList<String> list = new ArrayList<String>();
  String str01 = String("test1");
  String str02 = String("test2");
  list.add(test1);
  list.add(test2);
//方式二:
 ArrayList<String> list = new ArrayList<String>(){{add("test1"); add("test2");}};  

ArrayList在使用過程中需要注意的一個問題便是在遍歷的過程中刪除Item元素,該問題的關鍵在於面試者使用的是ArrayList的remove還是Iterator的remove,如果操作不當,會出現刪除的元素並沒有完全被刪除,或者出現ConcurrentModificationException異常。

//採用For迴圈進行刪除,該方法會造成某些物件沒有被刪除的情況
public class Test1 {  

    public static void main(String[] args) {  
        ArrayList<String> aList = new ArrayList<String>();  
        aList.add("a");  
        aList.add("ab");  
        aList.add("abc");  
        aList.add("abc");  
        aList.add("abcr");  
        aList.add("abc");  
        aList.add("abcf");  
        aList.add("abc");  
        aList.add("abdc");  

        for(int i = 0;i < aList.size();i++){  
            if(aList.get(i).equals("abc")){  
                aList.remove(i);   
            }  
        }            
        System.out.println(aList);  
    }  
}
//輸出結果為:[a, ab, abc, abcr, abcf, abdc],其中有一個abc沒有被刪除
//採用遍歷器進行刪除
Iterator<String> iter = aList.iterator();  
        while(iter.hasNext()){  
            if(iter.next().equals("abc")){  
                iter.remove();   
            }  
//輸出結果[a, ab, abcr, abcf, abdc]
//採用遍歷器進行刪除
 for (String tar : aList) {
            if (tar.equals("abc")){
                aList.remove(tar);
            }
        }
//結果是出現異常

綜上所述,在對ArrayList進行遍歷刪除時,建議使用遍歷器iterator進行遍歷刪除。

2.2 LinkedList
Android中的LinkedList是通過雙向列表實現的。連結串列元素除了含有自身的值以外,還含有上一個元素和下一個元素的引用。

private static final class Link<T> {
        T data;//當前值

        Link<T> previous, next;//上一個,和下個連結串列物件的引用

        Link(T o, Link<T> p, Link<T> n) {
            data = o;
            previous = p;
            next = n;
        }
    }

LinkedList的插入和刪除操作就是雙向連結串列的插入和刪除操作,參加如下:

//插入操作操作
private boolean addLastImpl(E object) {
        Link<E> oldLast = voidLink.previous;
      //新元素的頭指向上一個元素,尾指向連結串列的頭部
        Link<E> newLink = new Link<E>(object, oldLast, voidLink);
      //頭元素的頭指向新元素
        voidLink.previous = newLink;
       //上一個結尾元素的尾 指向新元素
        oldLast.next = newLink;
        size++;
        modCount++;
        return true;
    }
//刪除操作
@Override
    public E remove(int location) {
        if (location >= 0 && location < size) {
            Link<E> link = voidLink;
            if (location < (size / 2)) {
                for (int i = 0; i <= location; i++) {
                    link = link.next;
                }
            } else {
                for (int i = size; i > location; i--) {
                    link = link.previous;
                }
            }
            Link<E> previous = link.previous;
            Link<E> next = link.next;
            previous.next = next;
            next.previous = previous;
            size--;
            modCount++;
            return link.data;
        }
        throw new IndexOutOfBoundsException();
    }

LinkedList在實現List的介面的同時也實現了Queue 的介面。

2.3 Vector
Vector相當於具有同步操作的ArrayList,但是由於同步特性會造成一部分效能的損失。Vector基於陣列和同步特性,支援包括新增、移除和替換元素在內的所有操作,允許包括NULL在內元素。
2.4 HashSet
Set是最簡單的一種集合。集合中的物件不按特定的方式排序,並且沒有重複物件。HashSet類按照雜湊演算法來存取集合中的物件,存取速度比較快。一般會用HashSet對List中相同的元素進行去重,參考如下:

public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("a");
        list.add("f");
        list.add("b");
        list.add("c");
        list.add("a");
        list.add("d");
        System.out.println(list);

        list = removeSame(list);
        System.out.println(list);

    }

    private static List<String> removeSame(List<String> list) {
        Set<String> set = new HashSet<>();
        set.addAll(list);
        List<String> listSingle = new ArrayList<>();
        for(String s : set){
            listSingle.add(s);
        }
        return listSingle;
    }

2.5 TreeSet和LinkedHashSet
TreeSet儲存次序的Set,底層為樹結構。使用它可以從Set中提取有序的序列。
LinkedHashSet,具有HashSet的查詢速度,且內部使用連結串列維護元素的順序(插入的次序)。於是在使用迭代器遍歷Set時,結構會按元素插入的次序顯示。
2.6 Queue
Queue用於模擬“佇列”這種資料結構(先進先出)。Queue提供的操作函式如下:

入隊 出隊 檢索
offer(E e) poll() peek()
add(E e) remove() element()

Queue的實現類主要分為三類
非執行緒安全:LinkedList,PriorityQueue等。
執行緒安全:非阻塞ConcurrentLinkedQueue
執行緒安全,阻塞:BlockingQueue實現類:ArrayBlockingQueue,LinkedBlockingQueue,PriorityBlockingQueue。
Queue在Android開發中很多場景都會用到,比如線上程池的任務佇列就一般會用LinkedBlockingQueue來進行存放。

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(
                nThreads, nThreads,
                0L, TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>() //任務佇列採用LinkedBlockingQueue進行存放
        );

2.7 Map
Map除去平常程式碼中用到的儲存鍵值對的功能外,在此只總結一下Map在平常使用中的遍歷操作,參見如下:


// Map.values()遍歷所有的value,不遍歷key  
for (String v : map.values()) {  
    System.out.println("value= " + v);  
} 

//取二次值,先取key再取value,建議只需要用key的時候使用,節省時間、空間  
// keySet遍歷key和value  
for (String key : map.keySet()) {  
    System.out.println("key= "+ key + " and value= " + map.get(key));  
}

// 取一次值,一次把key和value全部取出  
// entrySet使用iterator遍歷key和value  
Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();  
while (it.hasNext()) {  
    Map.Entry<String, String> entry = it.next();  
    System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());  
}

// 推薦,尤其是容量大時,TreeMap尤其推薦  
// entrySet遍歷key和value  
for (Map.Entry<String, String> entry : map.entrySet()) {  
    System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());  
} 

三、Android中常用類其它資料結構

SparseArray型別,SparseArray有如下特性:

1SparseArray是android提供的一個工具類,它可以用來替代hashmap進行物件的儲存
2SparseArray比HashMap更省記憶體,它對資料採取了矩陣壓縮的方式來表示稀疏陣列的資料,從而節約記憶體空間
3SparseArray只能儲存key為整型的資料
4SparseArray在儲存和讀取資料時候,使用的是二分查詢法,提高了查詢的效率
5SparseArray有自己的垃圾回收機制。(當數量不是很多的時候,這個不必關心。)

SparseArray的插取

SparseArray<String> sparseArray = new android.util.SparseArray<String>(16);
sparseArray.put(10, "value");
sparseArray.get(10);

四、參考部落格

從原始碼看Android常用的資料結構 ( SDK23版本 ) ( 三 , Queue篇)
Android List,Set,Map集合安全 集合區別 併發集合類效能分析
Android中List、Set、Map資料結構詳解
List、Set、Map 和 Queue 之間的區別
Android之collection(集合)
java 利用HashSet去重並保持排序
Android中的HashMap,ArrayMap和SparseArray