Java常用數據結構

線性表,鏈表,哈希表是常用的數據結構,在JAVA開發時,JDK已經提供,包含在java.util包中。

Java集合(用來存儲實例的容器)框架主要是有Collection和Map兩個根接口及其子接口、實現類組成。大致可以分爲Set 、List、Map三種。Set代表無序、

不可重複的集合;List代表有序、重複的集合;Map則代表具有映射關係的集合。在Java5之後,增加了Queue集合,代表一種隊列集合。

1.Collection 接口


Collection接口是Set、List、Queue接口的父接口,Collection的遍歷可以使用Iterator或者for循環來實現,但是在邊遍歷邊刪除的時候必須使用Iterator.remove()。

Iterator<Object> objects =list.iterator();
   while(objects.hasNext()){
	objects.remove();
}

不要使用for循環方法,回報:ConcurrentModificationException錯誤,for(…)會自動生成一個iterator來遍歷objects,但同時objects正在被Iterator.remove()修改。

在Java中不允許一個線程在遍歷Collection的同時另一個線程在刪除它。

List接口


List接口是有序的,相比set接口,會多一些與索引位置有關的方法。

ArrayList Vector 都是基於數組實現的List類,是線性表,使用Object數組作爲容器去存儲數據。特點相比LinkedList的是查詢、遍歷速度快,刪除和插入速度慢。

ArrayList是線程不安全的,Vector是線程安全的,Vector提供了一個子類Stack,可以方便的模擬“棧”這種數據結構。Vector的性能會比ArrayList低,兩者有很多重複方法,不推薦使用Vector類,即使需要考慮同步,即也可以通過其它方法實現。同樣我們也可以通過ArrayDeque類或LinkedList類實現“棧”的相關功能。所以Vector與子類Stack,建議不在使用。

LinkedList基於鏈表實現的List類,是線程不安全的。特點是:相對於ArrayList是查詢遍歷速度慢,刪除插入速度快。

List 拷貝有兩種方法:一種使用ArrayList的構造器

Iterator<Object> objects =list.iterator();
   while(objects.hasNext()){
	objects.remove();
}

第二種是Collection.copy():該,方法的話目標list至少跟源list長度一樣長,否則

indexOutOfBoundsException
 List<String> mLists = new ArrayList<>();
        for (int i = 0; i < 4; i++) {
            mLists.add("name" + i);
        }
 List<String> reCopy =new ArrayList<>(mLists.size());
 Collections.copy(reCopy,mLists);


Set子接口

Set集合不允許包含相同的元素,判斷對象是否相同根據equals

HashSet:底層是基於哈希表(HashMap)數據結構存儲實例,爲快速查找而設計的set,特點:1.不能保證元素的排列順序,加入的元素要特別注意hashCode()方法的實現; 2. HashSet線程是不安全的,多線程訪問,需要手動同步。3.集合元素值可以是null。

public HashSet() {  
	map = new HashMap<>();  
		}  
		private static final Object PRESENT = new Object();  
		public boolean add(E e) {  
		        return map.put(e, PRESENT)==null;  
		}  

LinkedHashSet:底層是基於雙向鏈表數據結構來存儲數據結構的,特點:1.元素按照插入順序排序,元素除了根據hasCode值來決定元素的存儲位置,鏈表結構也在維護元素的位置,所以有序;2.性能低於HashSet,要維護元素的插入順序。

TreeSetSortedSet接口的實現類,保持次序的set,特點:1.實例都是有序的,按照排序順序;2底層的數據結構是紅黑二叉樹;3.元素是相互兼容的類型,不同類型的實例不具備比較性。這裏元素必須實現Comparable接口。可以定義比較器(Comparator)來實現自定義的排序

EnumSet:是專爲枚舉設計的集合類,EnumSet所有元素指定枚舉類型的枚舉值。

Queue子接口

Queue用於模擬隊列這種數據結構,實現“FIFO”等數據結構。通常,隊列不允許隨機訪問隊列中的元素。且並未定義阻塞隊列的方法,而這在併發編程中是很常見的。BlockingQueue 接口定義了那些等待元素出現或等待隊列中有可用空間的方法,這些方法擴展了此接口

Queue 實現通常不允許插入 null 元素,儘管某些實現(如 LinkedList)並不禁止插入 null。即使在允許 null 的實現中,也不應該將 null 插入到 Queue 中,因爲 null 也用作 poll 方法的一個特殊返回值,表明隊列不包含元素。

boolean add(E e) : 將元素加入到隊尾,不建議使用

boolean offer(E e): 將指定的元素插入此隊列(如果立即可行且不會違反容量限制),當使用有容量限制的隊列時,此方法通常要優於 add(E),後者可能無法插入元素,而只是拋出一個異常。推薦使用此方法取代add

E remove(): 獲取頭部元素並且刪除元素,不建議使用

E poll(): 獲取頭部元素並且刪除元素,隊列爲空返回null;推薦使用此方法取代remove

E element(): 獲取但是不移除此隊列的頭

E peek(): 獲取隊列頭部元素卻不刪除元素,隊列爲空返回null

public void testQueue() {
        Queue<String> queue = new LinkedList<String>();
        queue.offer("1.你在哪兒?");
        queue.offer("2.我在這裏。");
        queue.offer("3.那你又在哪兒呢?");
        String str = null;
        while ((str = queue.poll()) != null) {
            System.out.println(str);
        }
    }

PriorityQueue: 保存隊列元素的順序並不是按照加入序列的順序,是按照隊內元素的大小重新排序,當你調用peek()或是poll(),返回隊列中的最小元素。也可以自定義排序。

public void testPriorityQueue() {
        PriorityQueue<Integer> pq = new PriorityQueue<Integer>(20, new Comparator<Integer>() {
            public int compare(Integer i, Integer j) {
                // 對數字進行奇偶分類,然後比較返回;偶數有較低的返回值(對2取餘數然後相減),奇數直接相減。
                int result = i % 2 - j % 2;
                if (result == 0)
                    result = i - j;
                return result;
            }
        });

        // 倒序插入測試數據
        for (int i = 0; i < 20; i++) {
            pq.offer(20 - i);
        }

        // 打印結果,偶數因爲有較低的值,所以排在前面
        for (int i = 0; i < 20; i++) {
            System.out.println(pq.poll());
        }
    }

Deque子接口

Deque代表一個雙端隊列,可以當作一個雙端隊列使用,也可以當作“棧”來使用,因爲它包含出棧pop()與入棧push()方法。

ArrayDeque類是Deque的實現類,數組方式實現:

addFirst(Object o):元素增加至隊列開頭

addLast(Object o):元素增加至隊列末尾

poolFirst():獲取並刪除隊列第一個元素,隊列爲空返回null

poolLast():獲取並刪除隊列最後一個元素,隊列爲空返回null

pop():“棧”方法,出棧,相當於removeFirst()

push(Object o):“棧”方法,入棧,相當於addFirst()

removeFirst():獲取並刪除隊列第一個元素

removeLast():獲取並刪除隊列最後一個元素

補充說明:

LinkedList類是List接口的實現類,同時它也實現了Deque接口。因此它也可以當做一個雙端隊列來用,也可以當作“棧”來使用。並且,它是以鏈表的形式來實現的,這樣的結果是它的隨機訪問集合中的元素時性能較差,但插入與刪除操作性能非常出色。


2.Map接口:映射表,核心思想是鍵值關聯,存儲鍵值對,key是唯一的


HashTable 集成map接口,實現一個key-value映射的哈希表。任何非空的對象都可以作爲key或者value使用,是線程安全的

HashMap : Map 基於散列表的實現(取代了Hashtable),插入和查詢“鍵值對”的開銷是固定的,可以通過設置容量和負載因子,調整容器的性能。HashMap結構的實現原理是將put進來的key-value封裝成一個Entry對象存儲到一個Entry數組中,位置(數組下標)由key的哈希值與數組長度計算而來。它的特點:1. 它是線程不安全的,允許null,即null value和null key;2. 它是無需的;3.HashMap實際上是一個“鏈表散列”的數據結構,即數組和鏈表的結合體。從下圖中可以看出,HashMap底層就是一個數組結構,數組中的每一項又是一個鏈表。當新建一個HashMap的時候,就會初始化一個數組。


LinkedHashMap:類似於HashMap,HashMap的子類,但是它迭代遍歷時,取的“鍵值對”的順序是其插入的順序,或者是最少使(LRU)用的次序,只比HashMap慢一點,但是它的迭代速度更快,它是用來鏈表維護內部的次序。它的特點:1.底層結構是雙向鏈表;2.有序擺列。

Properties: 是HashTable的子類,一般用來讀取資源文件。資源文件後綴是.properties文件,內容採取的是鍵值對方式存儲。

TreeMap :由Entry對象爲節點組成的一顆紅黑樹,put到TreeMap的數據默認按key的自然順序排序,new TreeMap時傳入Comparator自定義排序。它是基於紅黑樹的實現,查看“鍵”或“鍵值對”時,它們會被排序(由Comparable或Comparator決定),得到的結果自然是排序的,它是唯一帶有subMap()方法的Map,可以返回一個子書。

WeakHashMap 是一種改進的HashMap(),它對key實行“弱引用”,如果如果一個key不再被外部所引用,那麼該key可以被GC回收


=====================================================》

一些使用方法的補充:

1.如何將List轉化成int[]?

很多人可能認爲只需用list.toArray()即可,其實不然。List.toArray()方法只可能得到Integer[],無法得到int[]。正確方法:

int[] array = new int[list.size()];
for(int i = 0; i < list.size(); i++){  
      array[i] = list.get(i);
}

2.如何將int[]轉化成List?

int[] array = {1,2,3,4,5};
List<Integer> list = new ArrayList<Integer>();
for(int i: array) { 
list.add(i);
}

3. 過濾一個Collection最好的方法是什麼?

如過濾掉list中大於5的整數。

Iterator<Integer> it = list.iterator();

while(it.hasNext()){
	int i = it.next();
	if(i > 5) {  //過濾掉大於5的整數
		it.remove(); 
	}
}
4. 將List轉化成Set最簡單的方法?

有兩種方法,取決於你怎麼要怎麼定義兩個元素相等。第一種方法是將list放入HashSet裏,該方法元素是否相等是通過它們的hashCode()來比較的。如果需要自己定義比較的方法,需要用TreeSet。

Set<Integer> set = new HashSet<Integer>(list);
Set<Integer> set = new TreeSet<Integer>(aComparator);
set.addAll(list);
5. 如何刪除ArrayList中重複的元素?

如果不關心元素在ArrayList中的順序,可以將list放入set中來刪除重複元素,然後在放回list。

Set<Integer> set = new HashSet<Integer>(list);
list.clear();
list.addAll(set);
如果關心元素在ArrayList中的順序,可以用LinkedHashSet。

6. 有序的collectionJava裏有很多方法來維持一個collection有序。有的需要實現Comparable接口,有的需要自己指定Comparator。

1.Collections.sort()可以用來對list排序。該排序是穩定的,並且可以保證nlog(n)的性能。

2.PriorityQueue提供排序的隊列。PriorityQueue和Collections.sort()的區別是,PriorityQueue動態維護一個有序的隊列(每添加或刪除一個元素就會重新排序),但是隻能獲隊列中的頭元素。

3.如果collection中沒有重複的元素,TreeSet是另一個選擇。跟PriorityQueue一樣的是,TreeSet也動態維護一個有序的集合。可以從TreeSet中獲取最大和最小的元素。

總結:Collections.sort()提供一個一次排序的list。PriorityQueue和TreeSet動態維護排序的collection。

發佈了34 篇原創文章 · 獲贊 2 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章