Java 常用集合框架List、Set、Map和Queue等體系彙總



一、集合框架

整體框架圖:

在這裏插入圖片描述

在這裏插入圖片描述

簡化版框架圖:

在這裏插入圖片描述

在這裏插入圖片描述

在這裏插入圖片描述


總體分析:

看上面的框架圖,先抓住它的主幹,即 CollectionMap

  • List , Set, Map 都是接口,前兩個繼承至 Collection 接口,Map爲獨立接口。

  • Collection 是一個接口,是高度抽象出來的集合,它包含了集合的基本操作和屬性。Collection 包含了 ListSet 兩大分支。

    (1)List 是一個有序的隊列,每一個元素都有它的索引。第一個元素的索引值是0。List 的實現類有 LinkedList , ArrayList, Vector, Stack

    (2)Set 是一個不允許有重複元素的集合。Set 的實現類有 HastSet、LinkedHashSet 和 TreeSet。HashSet 依賴於 HashMap,它實際上是通過 HashMap 實現的;TreeSet依賴於 TreeMap,它實際上是通過 TreeMap 實現的。

  • Map 是一個映射接口,即 key - value 鍵值對。Map 中的每一個元素包含 “一個key” 和 “key對應的value”。Map 下有 Hashtable,LinkedHashMap,HashMap,TreeMap

  • Iterator 是遍歷集合的工具,即我們通常通過 Iterator 迭代器來遍歷集合。我們說 Collection 依賴於 Iterator,是因爲 Collection 的實現類都要實現 iterator() 函數,返回一個 Iterator 對象。ListIterator 是專門爲遍歷 List 而存在的。

  • Enumeration 是 JDK 1.0 引入的抽象類。作用和 Iterator 一樣,也是遍歷集合;但是 Enumeration 的功能要比 Iterator 少。Enumeration只能在 Hashtable, Vector, Stack 中使用。

  • Collection 接口下還有個 Queue 接口,有 PriorityQueue 類。Queue 接口與 List、Set 同一級別,都是繼承了 Collection 接口。



二、Collection接口

Collection 接口是處理對象集合的根接口,其中定義了很多對元素進行操作的方法,其子接口也都可以使用這些方法。Collection 接口有三個主要的子接口 List、Set 和 Queue,注意Map不是Collection的子接口。

Collection接口的方法:
在這裏插入圖片描述
在這裏插入圖片描述
另外,Collection 中有一個 iterator() 函數,它的作用是返回一個 Iterator 接口。通常,我們通過 Iterator 迭代器來遍歷集合。ListIterator 是 List 接口所特有的,在 List 接口中,通過 ListIterator() 返回一個 ListIterator 對象。

下面介紹兩個主要的接口 List 和 Set。


1、List接口

List 接口繼承於 Collection 接口,它可以定義一個允許重複的有序集合。List 中的元素是有序的,所以我們可以通過索引來訪問 List 中的元素,這類似於Java的數組。 並且可以對列表中每個元素的插入位置進行精確地控制。實現 List 接口的集合主要有:ArrayList、LinkedList、Vector、Stack

在這裏插入圖片描述
在這裏插入圖片描述

(1)ArrayList

  • ArrayList 是一個動態數組,也是我們最常用的集合,非同步的。它允許任何符合規則的元素插入甚至包括 null。每一個 ArrayList 都有一個初始容量(10),該容量代表了數組的大小。隨着容器中的元素不斷增加,容器的大小也會隨着增加。

  • 它的隨機訪問速度是最快的,但是對隨機項的插入和刪除操作代價是比較昂貴的,除非變動是在 ArrayList 末端,原因是從數組中間刪除一個元素,所有的元素都要向數組的前端移動,同理,在數組中間插入一個元素也是如此。


(2)LinkedList

  • LinkedList 是一個雙向鏈表,實現了List接口,允許 null 元素。它除了有 ArrayList 的基本操作方法外還額外提供了get(),remove(),insert() 方法在 LinkedList 的首部或尾部操作。 這些操作使 LinkedList 可被用作堆棧(stack),隊列(queue)或雙向隊列(deque)。

  • 由於實現的方式不同,LinkedList 不能隨機訪問,它所有的操作都是要按照雙重鏈表的需要執行。在列表中索引的操作將從開頭或結尾遍歷列表(從靠近指定索引的一端)。這樣做的好處就是可以通過較低的代價在List中進行插入和刪除操作。

  • 與 ArrayList 一樣,LinkedList 也是非同步的。


(3)Vector

與 ArrayList 相似,但是 Vector 是同步的。所以說 Vector 是線程安全的動態數組。它的操作與 ArrayList幾乎一樣。


(4)Stack

Stack 繼承自Vector,實現一個後進先出的堆棧。Stack提供了 5 個額外的方法使得Vector得以被當作堆棧使用:

  • push() 和 pop() 方法,
  • peek() 方法得到棧頂的元素,
  • empty() 方法測試堆棧是否爲空,
  • search() 方法檢測一個元素在堆棧中的位置。


(5)使用示例

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

/*List接口的特點:
*   1. 它是一個元素存取有序的集合
*   2. 它是一個帶有索引的集合,通過索引就可以精確的操作集合中的元素(與數組的索引是一個道理)。
*   3. 集合中可以有重複的元素,通過元素的equals方法,來比較是否爲重複的元素。
* */
public class Demo01List {
    public static void main(String[] args) {
        //通過多態的方法來是實現,List作爲一個接口不能直接被使用,可以通過ArrayList子類來使用
        List<String> list = new ArrayList<>();

        list.add("唐僧");//從尾部添加
        list.add("孫悟空");
        list.add("豬八戒");
        System.out.println(list);
        list.add(1,"華蒙");//從指定位置添加
        System.out.println(list);

        System.out.println("刪除索引位置爲2的元素");
        System.out.println(list.remove(2));//刪除索引位置爲2的元素,並返回刪除的元素

        list.set(0, "三毛");//將指定位置的元素替換爲新元素
        System.out.println(list);

        for(int i = 0;i<list.size();i++){
            System.out.println(list.get(i));//get(int i) 返回指定位置的元素
        }

    }
}

//輸出結果:
[唐僧, 孫悟空, 豬八戒]
[唐僧, 華蒙, 孫悟空, 豬八戒]
刪除索引位置爲2的元素
孫悟空
[三毛, 華蒙, 豬八戒]
三毛
華蒙
豬八戒



2、Set接口

  • Set 是一種不包含重複的元素的無序集合 ,即任意的兩個元素 e1 和 e2 都有e1.equals(e2) == false。Set 最多有一個 null 元素。

  • 雖然Set中元素沒有順序,但是元素在set中的位置是由該元素的HashCode決定的,其具體位置其實是固定的。

  • Set接口有三個具體實現類,分別是散列集HashSet、鏈式散列集LinkedHashSet和樹形集TreeSet。

  • 在set接口中的不重複是有特殊要求的。比如:對象A和對象B,本來是不同的兩個對象,正常情況下它們是能夠放入到 Set 裏面的,但是如果對象 A 和 B 都重寫了 hashcode() 和 equals() 方法,並且重寫後的 hashcode() 和 equals() 方法是相同的話,那麼 A 和 B 是不能同時放入到 Set 集合中去的,也就是 Set 集合中的去重和 hashcode() 與 equals() 方法直接相關。


(1)HashSet

  • HashSet 是一個沒有重複元素的集合。它是由 HashMap 實現的,無序,即元素插入的順序與輸出的順序不一致

  • HashSet 允許使用 null 元素,但最多存入一個。HashSet 是非同步的

  • HashSet 按 Hash 算法來存儲集合的元素,因此具有很好的存取和查找性能

  • HashSet 通過一個 HashMap 存儲元素,元素是存放在 HashMap 的 Key 中,而 Value 統一使用一個 Object 對象

  • HashSet 中存儲的元素的是無序的,但是由於 HashSet 底層是基於 Hash 算法實現的,使用了 hashcode ,所以 HashSet 中相應的元素的位置是固定的

  • 如果 Set 中的可變元素改變了自身狀態導致 Object.equals(Object) == true 將導致一些問題。

  • HashSet 依賴 hashCode() 和 equals() 來保證元素唯一性。


(2)LinkedHashSet

  • LinkedHashSet 繼承自 HashSet ,其底層是基於 LinkedHashMap 來實現的非同步

  • HashSet 是無序的,而 LinkedHashSet 是有序的

  • LinkedHashSet 集合同樣是根據元素的 hashCode() 值來決定元素的存儲位置,但是它同時使用鏈表維護元素的次序。LinkedHashSet 將會以元素的添加順序訪問集合的元素

  • LinkedHashSet 集合底層數據結構是鏈表和哈希表。由鏈表保證元素有序,由哈希表保證元素唯一


(3)TreeSet

  • TreeSet 是一個有序集合,其底層是基於 TreeMap 實現的,非線程安全

  • TreeSet 的底層數據結構採用二叉樹來實現,元素唯一且已經排好序。

  • TreeSet 可以確保集合元素處於排序狀態。TreeSet支持兩種排序方式:自然排序和定製排序,其中自然排序爲默認的排序方式。當我們構造 TreeSet 時,若使用不帶參數的構造函數,則 TreeSet 使用自然比較器;若用戶需要使用自定義的比較器,則需要使用帶比較器的參數。

  • TreeSet 集合不是通過 hashcode 和 equals 函數來比較元素的。它是通過 compare 或者 comparaeTo 函數來判斷元素是否相等。compare 函數通過判斷兩個對象的 id,若相同則判斷爲重複元素,不會被加入到集合中。


(4)List 和 Set 適用場景

在這裏插入圖片描述



(5)使用示例

package com.test;  
  
import java.util.HashSet;  
import java.util.LinkedHashSet;  
import java.util.TreeSet;  
  
/**  
 * @description 幾個set的比較  
 *    HashSet:哈希表是通過使用稱爲散列法的機制來存儲信息的,元素並沒有以某種特定順序來存放;  
 *    LinkedHashSet:以元素插入的順序來維護集合的鏈接表,允許以插入的順序在集合中迭代;  
 *    TreeSet:提供一個使用樹結構存儲Set接口的實現,對象以升序順序存儲,訪問和遍歷的時間很快。  
 * @author Zhou-Jingxian  
 *  
 */  
public class SetDemo {  
  
    public static void main(String[] args) {  
  
        HashSet<String> hs = new HashSet<String>();  
        hs.add("B");  
        hs.add("A");  
        hs.add("D");  
        hs.add("E");  
        hs.add("C");  
        hs.add("F");  
        System.out.println("HashSet 順序:\n"+hs);  
          
        LinkedHashSet<String> lhs = new LinkedHashSet<String>();  
        lhs.add("B");  
        lhs.add("A");  
        lhs.add("D");  
        lhs.add("E");  
        lhs.add("C");  
        lhs.add("F");  
        System.out.println("LinkedHashSet 順序:\n"+lhs);  
          
        TreeSet<String> ts = new TreeSet<String>();  
        ts.add("B");  
        ts.add("A");  
        ts.add("D");  
        ts.add("E");  
        ts.add("C");  
        ts.add("F");  
        System.out.println("TreeSet 順序:\n"+ts);  
    }  
}


//輸出結果:
HashSet 順序:[D, E, F, A, B, C]
LinkedHashSet 順序:[B, A, D, E, C, F]
TreeSet 順序:[A, B, C, D, E, F]



3、Queue接口

  • 隊列是一種特殊的線性表,它只允許在表的前端進行刪除操作,而在表的後端進行插入操作,即 FIFO(先進先出)特點。

  • Queue 接口提供了支持add,remove操作,但是不建議使用,一般用 offer(),poll() 和 peek() 方法來替代。

  • LinkedList 類實現了 Queue 接口,因此也可以把 LinkedList 當成 Queue 來用。隊列不允許插入 null 元素,但是 LinkedList 可以。


實現 Queue 和 Deque 接口的類圖:
在這裏插入圖片描述

Queue接口常用方法:

- add(E e) 插入一個元素到隊列中,失敗時拋出異常 IllegalStateException (隊列容量不夠)
- remove() 返回並移除隊列的頭部元素,隊列爲空時, 拋出異常
- element() 返回隊列頭部的元素,隊列爲空時, 拋出異常
- offer(E e) 插入一個元素到隊列中,失敗時返回false
- peek() 返回隊列頭部的元素,隊列爲空時返回null
- poll() 返回並移除隊列的頭部元素,隊列爲空時返回null



(1)Deque接口

  • Deque 表示雙端隊列,雙端隊列是在兩端都可以進行插入和刪除的隊列。

  • Deque 是一個比 Stack 和 Queue 功能更強大的接口,它同時實現了棧和隊列的功能。

  • ArrayDeque 和 LinkeList 實現了 Deque 接口。


Dueue接口常用方法:

  • add(E e) 將新元素添加到隊列的尾端(當不超過隊列的容量時)
  • addFirst(E e) 將新元素添加到隊列的頭部
  • addLast(E e) 將新元素添加到隊列的尾部
  • contains(Object o) 雙端隊列是否含有對象o
  • descendingIterator()倒敘返回隊列的迭代器
  • element() 返回隊列的頭部元素
  • getFirst() 獲取頭部元素
  • getLast() 獲取尾部元素
  • iterator() 迭代隊列
  • offer(E e) 將新元素插入到隊列尾部
  • offerFirst(E e) 將新元素添加到隊列的頭部
  • offerLast(E e) 將新元素添加到隊列的尾部
  • peek() 返回隊列的頭部元素
  • peekFirst() 獲取頭部元素
  • peekLast() 獲取尾部元素
  • pool() 返回並移除隊列的頭部元素
  • poolFirst() 獲取並移除頭部元素
  • poolLast() 獲取並移除尾部元素
  • pop() 將一個元素出棧
  • push(E e) 講一個元素壓入棧
  • remove() 移除隊列的頭部元素
  • remove(Object o) 移除隊列中第一個o
  • removeFirst() 移除隊列的頭部元素
  • removeFirstOccurrence(Object o) 移除隊列中第一個o
  • removeLast() 移除隊列的尾部元素
  • removeLastOccurrence(Object o) 移除隊列中最後一個o
  • size()

實現Queue和Deque接口的類:
在這裏插入圖片描述



ArrayDeque類

ArrayDeque 類是雙端隊列的數組實現類,底層數據存儲在數組中,數組雙端隊列是可擴容的。由於沒有額外的功能,這裏不詳細討論了。


使用示例:

import java.util.*;
public class QueueDemo {
    public static void main(String[] args) {
       ArrayDeque<String> ad = new ArrayDeque<String>();

        ad.add("D");
        ad.offerFirst("E");
        ad.addFirst("F");
        ad.offer("A");
        ad.addLast("B");
        ad.offerLast("C");

        System.out.println("隊列中的元素:" + ad.toString());
        System.out.println("隊列中的第一個元素:" + ad.peekFirst());
        System.out.println("移除隊列中的元素:" + ad.remove());
        System.out.println("隊列中的元素:" + ad.toString());
        System.out.println("移除隊列中的元素:" + ad.pollLast());
        System.out.println("隊列中的元素:" + ad.toString());
    }  
}
// 隊列中的元素:[A, B, C, E, D, F]
// 隊列中的第一個元素:A
// 移除隊列中的元素:A
// 隊列中的元素:[B, D, C, E, F]
// 移除隊列中的元素:B
// 隊列中的元素:[C, D, F, E]


(2)PriorityQueue類

  • 優先級隊列是不同於先進先出隊列的另一種隊列。每次從隊列中取出的是具有最高優先權的元素。

  • 邏輯上使用堆結構(完全二叉樹)實現,物理上使用動態數組實現,並非像TreeMap一樣完全有序,但是如果按照指定方式出隊,結果可以是有序的。

  • 如果不提供Comparator的話,優先隊列中元素默認按自然順序排列,也就是數字默認是小的在隊列頭,字符串則按字典序排列。


使用示例1:

import java.util.*;
public class QueueDemo {
    public static void main(String[] args) {
        PriorityQueue<String> pq = new PriorityQueue<String>();

        // 隊列中的元素按照自然順序排序,元素加入的順序與元素在隊列中的順序無關
        pq.add("D");
        pq.add("E");
        pq.add("F");
        pq.add("A");
        pq.add("B");
        pq.add("C");

        // 輸出隊列中的元素,不保證以任何特定的順序遍歷優先級隊列中的元素。
        // toString方法中使用了迭代器。
        System.out.println("隊列中的元素:" + pq.toString());
        System.out.println("隊列中的第一個元素:" + pq.peek());
        System.out.println("移除隊列中的元素:" + pq.remove());
        System.out.println("隊列中的元素:" + pq.toString());
        System.out.println("移除隊列中的元素:" + pq.poll());
        System.out.println("隊列中的元素:" + pq.toString());
    }  
}
// 隊列中的元素:[A, B, C, E, D, F]
// 隊列中的第一個元素:A
// 移除隊列中的元素:A
// 隊列中的元素:[B, D, C, E, F]
// 移除隊列中的元素:B
// 隊列中的元素:[C, D, F, E]

使用示例2:

import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.Queue;
 
public class test {
	private String name;
	private int population;
	public test(String name, int population)
	{
		this.name = name;
	    this.population = population;
	}
	public String getName()
	{
	     return this.name;
	}
 
	public int getPopulation()
	{
	     return this.population;
	}
	public String toString()
	{
	     return getName() + " - " + getPopulation();
	}
	public static void main(String args[])
	{
		Comparator<test> OrderIsdn =  new Comparator<test>(){
			public int compare(test o1, test o2) {
				// TODO Auto-generated method stub
				int numbera = o1.getPopulation();
				int numberb = o2.getPopulation();
				if(numberb > numbera)
				{
					return 1;
				}
				else if(numberb<numbera)
				{
					return -1;
				}
				else
				{
					return 0;
				}
			
			}
 
			
			
		};
		Queue<test> priorityQueue =  new PriorityQueue<test>(11,OrderIsdn);
		
		test t1 = new test("t1",1);
		test t3 = new test("t3",3);
		test t2 = new test("t2",2);
		test t4 = new test("t4",0);
		priorityQueue.add(t1);
		priorityQueue.add(t3);
		priorityQueue.add(t2);
		priorityQueue.add(t4);
		System.out.println(priorityQueue.poll().toString());
	}
}


(3)使用示例

import java.util.LinkedList;
import java.util.Queue;
 
public class Main {
    public static void main(String[] args) {
        //add()和remove()方法在失敗的時候會拋出異常(不推薦)
        Queue<String> queue = new LinkedList<String>();
        //添加元素
        queue.offer("a");
        queue.offer("b");
        queue.offer("c");
        queue.offer("d");
        queue.offer("e");
        for(String q : queue){
            System.out.println(q);
        }
        System.out.println("===");
        System.out.println("poll="+queue.poll()); //返回第一個元素,並在隊列中刪除
        for(String q : queue){
            System.out.println(q);
        }
        System.out.println("===");
        System.out.println("element="+queue.element()); //返回第一個元素 
        for(String q : queue){
            System.out.println(q);
        }
        System.out.println("===");
        System.out.println("peek="+queue.peek()); //返回第一個元素 
        for(String q : queue){
            System.out.println(q);
        }
    }
}

//輸出結果:
a
b
c
d
e
===
poll=a
b
c
d
e
===
element=b
b
c
d
e
===
peek=b
b
c
d
e



三、Map接口

  • Map 與 List、Set 接口不同,它是由一系列鍵值對組成的集合,提供了 key 到 Value 的映射,同時它也沒有繼承Collection

  • 在 Map 中它保證了 key 與 value 之間的一一對應關係,也就是說一個 key 對應一個value,所以它不能存在相同的 key 值,當然 value 值可以相同。

  • Map 接口有四種主要的實現類,分別是 HashMap、Hashtable、LinkedHashMap 和 TreeMap

  • TreeMap 是有序的,HashMap 和 HashTable 是無序的。

  • Hashtable 的方法是同步的,HashMap 的方法不是同步的,這是兩者最主要的區別。

在這裏插入圖片描述


(1)HashMap 和 Hashtable

  • Hashtable 是線程安全的,HashMap 不是線程安全的

  • HashMap 效率較高,Hashtable 效率較低。如果對同步性或與遺留代碼的兼容性沒有任何要求,建議使用 HashMap。 查看 Hashtable 的源代碼就可以發現,除構造函數外,Hashtable 的所有 public 方法聲明中都有 synchronized 關鍵字,而 HashMap 的源碼中則沒有。

  • Hashtable 不允許 null 值,HashMap 允許 null 值(key和value都允許)

  • Hashtable 的父類是 Dictionary,HashMap 的父類是 AbstractMap 。

在這裏插入圖片描述

簡單示例:

	Hashtable numbers = new Hashtable();
	numbers.put(“one”, new Integer(1));
	numbers.put(“two”, new Integer(2));
	numbers.put(“three”, new Integer(3));
	// 要取出一個數,比如2,用相應的key:
	Integer n = (Integer)numbers.get(“two”);
	System.out.println(“two =+ n);

(2)LinkedHashMap

  • LinkedHashMap 是 HashMap 的一個子類,它保留插入的順序,如果需要輸出的順序和輸入時的相同,那麼就選用 LinkedHashMap

  • LinkedHashMap 是 Map 接口的哈希表和鏈接列表實現的,具有可預知的迭代順序。此實現提供所有可選的映射操作,並 允許使用 null 值和 null 鍵。此類不保證映射的順序,特別是它不保證該順序恆久不變。

  • LinkedHashMap 實現與 HashMap 的不同之處在於,LinkedHashMap 維護着一個運行於所有條目的雙重鏈接列表。

  • 根據鏈表中元素的順序可以分爲:按插入順序的鏈表按訪問順序(調用get方法)的鏈表 。默認是按插入順序排序,如果指定按訪問順序排序,那麼調用 get() 方法後,會將這次訪問的元素移至鏈表尾部,不斷訪問可以形成按訪問順序排序的鏈表。

  • LinkedHashMap 實現不是同步的。如果多個線程同時訪問鏈接的哈希映射,而其中至少一個線程從結構上修改了該映射,則它必須保持外部同步。

  • 由於LinkedHashMap需要維護元素的插入順序,因此性能略低於HashMap的性能,但在迭代訪問Map裏的全部元素時將有很好的性能,因爲它以鏈表來維護內部順序。


(3)TreeMap

  • TreeMap 是一個有序的 key-value 集合,非同步基於紅黑樹實現,每一個 key-value 節點作爲紅黑樹的一個節點。

  • TreeMap 存儲時會進行排序的,會根據 key 來對 key-value 鍵值對進行排序,其中排序方式也是分爲兩種,一種是自然排序,一種是定製排序,具體取決於使用的構造方法。

    • 自然排序: TreeMap 中所有的 key 必須實現 Comparable 接口,並且所有的 key 都應該是同一個類的對象,否則會報 ClassCastException 異常

    • 定製排序: 定義 TreeMap 時,創建一個 comparator 對象,該對象對所有的 TreeMap 中所有的 key 值進行排序,採用定製排序的時候不需要 TreeMap 中所有的 key 必須實現 Comparable 接口

  • TreeMap 判斷兩個元素相等的標準:兩個 key 通過 compareTo() 方法返回 0 ,則認爲這兩個 key 相等。

  • 如果使用自定義的類來作爲 TreeMap 中的 key 值,且想讓 TreeMap 能夠良好的工作,則必須重寫自定義類中的 equals() 方法,TreeMap 中判斷相等的標準是:兩個 key 通過 equals() 方法返回爲 true,並且通過 compareTo() 方法比較應該返回爲 0 。

在這裏插入圖片描述

a. 自然排序

自然排序要進行以下操作:

  • Student類中實現 Comparable 接口

  • 重寫 Comparable 接口中的 CompareTo() 方法


測試類1:

public class MyClass {
    public static void main(String[] args) {
        TreeSet<Student> ts=new TreeSet<Student>();
        //創建元素對象
        Student s1=new Student("zhangsan",20);
        Student s2=new Student("lis",22);
        Student s3=new Student("wangwu",24);
        Student s4=new Student("chenliu",26);
        Student s5=new Student("zhangsan",22);
        Student s6=new Student("qianqi",24);

        //將元素對象添加到集合對象中
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);
        ts.add(s6);

        //遍歷
        for(Student s:ts){
            System.out.println(s.getName()+"-----------"+s.getAge());
        }
    }
}

Student 類:

public class Student implements Comparable<Student>{
    private String name;
    private int age;

    public Student() {
        super();
        // TODO Auto-generated constructor stub
    }

    public Student(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    //compareTo(T o)  比較此對象與指定對象的順序。
    public int compareTo(Student s) {
        //return -1; //-1表示放在紅黑樹的左邊,即逆序輸出
        //return 1;  //1表示放在紅黑樹的右邊,即順序輸出
        //return o;  //表示元素相同,僅存放第一個元素
        //主要條件 姓名的長度,如果姓名長度小的就放在左子樹,否則放在右子樹
        int num=this.name.length()-s.name.length();
        //姓名的長度相同,不代表內容相同,如果按字典順序此 String 對象位於參數字符串之前,則比較結果爲一個負整數。
        //如果按字典順序此 String 對象位於參數字符串之後,則比較結果爲一個正整數。
        //如果這兩個字符串相等,則結果爲 0
        int num1=num==0?this.name.compareTo(s.name):num;
        //姓名的長度和內容相同,不代表年齡相同,所以還要判斷年齡
        int num2=num1==0?this.age-s.age:num1;
        return num2;
    }
}


輸出結果:

lis-----------22
qianqi-----------24
wangwu-----------24
chenliu-----------26
zhangsan-----------20
zhangsan-----------22

b. 比較器排序

比較器排序步驟:

  • 單獨創建一個比較類,這裏以MyComparator爲例,並且要讓其繼承Comparator接口

  • 重寫Comparator接口中的Compare方法

    compare(T o1,T o2)      比較用來排序的兩個參數。
    
  • 在主類中使用下面的 構造方法

    // 構造一個新的空 TreeSet,它根據指定比較器進行排序。
    TreeSet(Comparator<? superE> comparator)
    

測試類:

public class MyClass {

    public static void main(String[] args) {
        //創建集合對象
        //TreeSet(Comparator<? super E> comparator) 構造一個新的空 TreeSet,它根據指定比較器進行排序。
        TreeSet<Student> ts=new TreeSet<Student>(new MyComparator());

        //創建元素對象
        Student s1=new Student("zhangsan",20);
        Student s2=new Student("lis",22);
        Student s3=new Student("wangwu",24);
        Student s4=new Student("chenliu",26);
        Student s5=new Student("zhangsan",22);
        Student s6=new Student("qianqi",24);

        //將元素對象添加到集合對象中
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);
        ts.add(s6);

        //遍歷
        for(Student s:ts){
            System.out.println(s.getName()+"-----------"+s.getAge());
        }
    }
}


Student 類:

public class Student {
    private String name;
    private int age;

    public Student() {
        super();
        // TODO Auto-generated constructor stub
    }

    public Student(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

}

MyComparator 類:

public class MyComparator implements Comparator<Student> {

    @Override
    public int compare(Student s1,Student s2) {
        // 姓名長度
        int num = s1.getName().length() - s2.getName().length();
        // 姓名內容
        int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
        // 年齡
        int num3 = num2 == 0 ? s1.getAge() - s2.getAge() : num2;
        return num3;
    }

}

運行結果:

lis-----------22
qianqi-----------24
wangwu-----------24
chenliu-----------26
zhangsan-----------20
zhangsan-----------22


(4)使用示例

import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.TreeMap;

public class MapTest {

    public static void main(String[] args) {

        //HashMap
        HashMap<String,String> hashMap = new HashMap();
        hashMap.put("4", "d");
        hashMap.put("3", "c");
        hashMap.put("2", "b");
        hashMap.put("1", "a");

        Iterator<String> iteratorHashMap = hashMap.keySet().iterator();

        System.out.println("HashMap-->");

        while (iteratorHashMap.hasNext()){

            Object key1 = iteratorHashMap.next();
            System.out.println(key1 + "--" + hashMap.get(key1));
        }

        //LinkedHashMap
        LinkedHashMap<String,String> linkedHashMap = new LinkedHashMap();
        linkedHashMap.put("4", "d");
        linkedHashMap.put("3", "c");
        linkedHashMap.put("2", "b");
        linkedHashMap.put("1", "a");

        Iterator<String> iteratorLinkedHashMap = linkedHashMap.keySet().iterator();

        System.out.println("LinkedHashMap-->");

        while (iteratorLinkedHashMap.hasNext()){

            Object key2 = iteratorLinkedHashMap.next();
            System.out.println(key2 + "--" + linkedHashMap.get(key2));
        }

        //TreeMap
        TreeMap<String,String> treeMap = new TreeMap();
        treeMap.put("4", "d");
        treeMap.put("3", "c");
        treeMap.put("2", "b");
        treeMap.put("1", "a");

        Iterator<String> iteratorTreeMap = treeMap.keySet().iterator();

        System.out.println("TreeMap-->");

        while (iteratorTreeMap.hasNext()){

            Object key3 = iteratorTreeMap.next();
            System.out.println(key3 + "--" + treeMap.get(key3));
        }

    }

}



//輸出結果:
HashMap-->
3--c
2--b
1--a
4--d
LinkedHashMap-->
4--d
3--c
2--b
1--a
TreeMap-->
1--a
2--b
3--c
4--d



四、Iterator 與 ListIterator 詳解

1、Iterator

Iterator 是一個接口,它是集合的迭代器。集合可以通過 Iterator 去遍歷集合中的元素。Iterator提供的API接口如下:

  • boolean hasNext():判斷集合裏是否存在下一個元素,若有則返回 true。
  • Object next():返回集合裏下一個元素。
  • void remove():刪除集合裏上一次next方法返回的元素。

使用示例:

/*

Iterator的定義如下:
public interface Iterator<E> {}

*/

public class IteratorExample {
    public static void main(String[] args) {
        ArrayList<String> a = new ArrayList<String>();
        a.add("aaa");
        a.add("bbb");
        a.add("ccc");
        System.out.println("Before iterate : " + a);
        Iterator<String> it = a.iterator();
        while (it.hasNext()) {
            String t = it.next();
            if ("bbb".equals(t)) {
                it.remove();
            }
        }
        System.out.println("After iterate : " + a);
    }
}

//輸出結果:
Before iterate : [aaa, bbb, ccc]
After iterate : [aaa, ccc] 


注意:

  • Iterator只能單向移動。

  • Iterator.remove() 是唯一安全的方式來在迭代過程中修改集合;如果在迭代過程中以任何其它的方式修改了基本集合將會產生未知的行爲。而且每調用一次 next() 方法, remove() 方法只能被調用一次,如果違反這個規則將拋出一個異常。


2、ListIterator

  • ListIterator 是一個功能更加強大的迭代器, 它繼承於 Iterator 接口,只能用於各種 List 類型的訪問。

  • 可以通過調用 listIterator() 方法產生一個指向 List 開始處的 ListIterator。

  • 還可以調用 listIterator(n) 方法創建一個一開始就指向列表索引爲 n 的元素處的 ListIterator。

ListIterator接口定義如下:

public interface ListIterator<E> extends Iterator<E> {
    boolean hasNext();
 
    E next();
 
    boolean hasPrevious();
 
    E previous();
 
    int nextIndex();
 
    int previousIndex();
 
    void remove();
 
    void set(E e);
 
    void add(E e);
     
}

由以上定義我們可以推出 ListIterator 可以:

  • 雙向移動(向前 / 向後遍歷)

  • 產生相對於迭代器在列表中指向的當前位置的前一個和後一個元素的索引

  • 可以使用 set() 方法替換它訪問過的最後一個元素

  • 可以使用 add() 方法在 next() 方法返回的元素之前或 previous() 方法返回的元素之後插入一個元素

使用示例:

public class ListIteratorExample {
 
    public static void main(String[] args) {
        ArrayList<String> a = new ArrayList<String>();
        a.add("aaa");
        a.add("bbb");
        a.add("ccc");
        System.out.println("Before iterate : " + a);
        ListIterator<String> it = a.listIterator();
        while (it.hasNext()) {
            System.out.println(it.next() + ", " + it.previousIndex() + ", " + it.nextIndex());
        }
        while (it.hasPrevious()) {
            System.out.print(it.previous() + " ");
        }
        System.out.println();
        it = a.listIterator(1);
        while (it.hasNext()) {
            String t = it.next();
            System.out.println(t);
            if ("ccc".equals(t)) {
                it.set("nnn");
            } else {
                it.add("kkk");
            }
        }
        System.out.println("After iterate : " + a);
    }
}


//輸出結果:
Before iterate : [aaa, bbb, ccc]
aaa, 0, 1
bbb, 1, 2
ccc, 2, 3
ccc bbb aaa 
bbb
ccc
After iterate : [aaa, bbb, kkk, nnn]



五、Collection 和 Collections 的區別


1、Collection

  • java.util.Collection 是一個集合接口(集合類的一個頂級接口)。

  • 它提供了對集合對象進行基本操作的通用接口方法。

  • Collection接口在Java 類庫中有很多具體的實現。Collection接口的意義是爲各種具體的集合提供了最大化的統一操作方式,其直接繼承接口有 List 與 Set。


關係如下:

  • Collection
    • List
      • LinkedList
      • ArrayList
      • Vector
        • Stack
    • Set


2、Collections

  • java.util.Collections 是一個包裝類(工具類/幫助類)。

  • 它包含有各種有關集合操作的靜態多態方法

  • 此類不能實例化,就像一個工具類,用於對集合中元素進行排序、搜索以及線程安全等各種操作,服務於 Java 的 Collection 框架。


使用示例:

import java.util.ArrayList; 
import java.util.Collections; 
import java.util.List; 
  
public class TestCollections { 
      
    public static void main(String args[]) { 
        //注意List是實現Collection接口的 
        List list = new ArrayList(); 
        double array[] = { 112, 111, 23, 456, 231 }; 
        for (int i = 0; i < array.length; i++) { 
            list.add(new Double(array[i])); 
        } 
        Collections.sort(list); 
        for (int i = 0; i < array.length; i++) { 
            System.out.println(list.get(i)); 
        } 
        // 結果:23.0 111.0 112.0 231.0 456.0 
    } 
}


參考:

常用的幾種java集合類總結

Java集合中List,Set以及Map等集合體系詳解(史上最全)

java集合超詳解

java集合框架綜述

Java集合之Queue接口

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章