Java基礎(chǔ)——集合類 左Collection,右Map。

集合類簡介

為什么出現(xiàn)集合類?
面向?qū)ο笳Z言對事物的體現(xiàn)都是以對象的形式,所以為了方便對多個(gè)對象的操作,就要對對象進(jìn)行存儲(chǔ),集合就是存儲(chǔ)對象最常用的一種方式。

集合只用于存儲(chǔ)對象,集合長度是可變的,集合可以存儲(chǔ)不同類型的對象。

集合與數(shù)組
數(shù)組雖然也可以存儲(chǔ)對象,但長度是固定的;集合長度是可變的。數(shù)組中可以存儲(chǔ)任意數(shù)據(jù)類型,集合只能存儲(chǔ)對象。

集合類簡單結(jié)構(gòu)圖

集合類總的來說可以分類2大類,一類是Collection,另外一類是Map。

jhl.png

Collection 接口是最基本的集合接口,它不提供直接的實(shí)現(xiàn),Java SDK提供的類都是繼承自 Collection 的“子接口”如 List 和 Set。

在 Java 中所有實(shí)現(xiàn)了 Collection 接口的類都必須提供兩套標(biāo)準(zhǔn)的構(gòu)造函數(shù),
一個(gè)是無參,用于創(chuàng)建一個(gè)空的 Collection,
一個(gè)是帶有 Collection 參數(shù)的有參構(gòu)造函數(shù),用于創(chuàng)建一個(gè)新的 Collection,這個(gè)新的 Collection 與傳入進(jìn)來的 Collection 具備相同的元素。

實(shí)現(xiàn)了Collection接口的List接口
List 接口為 Collection 直接接口。List 所代表的是有序的 Collection,即它用某種特定的插入順序來維護(hù)元素順序。用戶可以對列表中每個(gè)元素的插入位置進(jìn)行精確地控制,同時(shí)可以根據(jù)元素的整數(shù)索引(在列表中的位置)訪問元素,并搜索列表中的元素。實(shí)現(xiàn) List 接口的集合主要有:ArrayList、LinkedList、Vector、Stack

實(shí)現(xiàn)了Collection接口的Set接口
Set 是一種不包括重復(fù)元素的 Collection。它維持它自己的內(nèi)部排序,所以隨機(jī)訪問沒有任何意義。與 List 一樣,它同樣運(yùn)行 null 的存在但是僅有一個(gè)。由于 Set 接口的特殊性,所有傳入 Set 集合中的元素都必須不同,同時(shí)要注意任何可變對象,如果在對集合中元素進(jìn)行操作時(shí),導(dǎo)致e1.equals(e2)==true,則必定會(huì)產(chǎn)生某些問題。實(shí)現(xiàn)了 Set 接口的集合有:EnumSet、HashSet、TreeSet

關(guān)于Collection的子接口List和Set后文會(huì)詳細(xì)描述,現(xiàn)在我們先來看一下 Collection 接口里面有那些基本方法

Collection的基本方法

  • boolean add(Object o):該方法用于向集合里面添加一個(gè)元素,若集合對象被添加操作改變了,返回true.

  • boolean addAll(Collection c):把集合c里面的所有元素添加到指定集合里面去,如果集合對象被添加操作改變了返回true.

  • void clear():清除集合里面的所有元素,將集合長度變?yōu)?。

  • boolean contains(Object o):返回集合里是否包含指定的元素。

  • boolean containsAll(Collection c):返回集合里是否包含集合c內(nèi)所有的元素。

  • boolean isEmpty():返回集合是否為空(長度是否為0)。

  • Iterator iterator():返回一個(gè)Iterator對象,用于遍歷集合里的元素。

  • boolean remove(Object o):刪除集合中指定元素o。

  • boolean removeAll(Collection c):從集合中刪除集合c里面的元素。若刪除一個(gè)或以上返回true。

  • boolean retainAll(Collection c):從集合中刪除集合c里不包含的元素。

  • int size():得到集合元素的個(gè)數(shù)。

  • Object[] toArray():把集合轉(zhuǎn)成一個(gè)數(shù)組,所有集合元素編程數(shù)組元素。

.
.
.

二、Colletion —— List

二.1 List 接口 (有序可重復(fù))

List 是有序的 Collection,使用此接口能夠精確的控制每個(gè)元素插入的位置。用戶能夠使用索引(元素在 List 中的位置,類似于數(shù)組下標(biāo))來訪問 List 中的元素,這類似于 Java 的數(shù)組。

和下面要提到的 Set 不同,List 允許有相同的元素。

除了具有 Collection 接口必備的 iterator()方法外,List 還提供一個(gè) listIterator()方法,返回一個(gè) ListIterator 接口,和標(biāo)準(zhǔn)的 Iterator 接口相比,ListIterator 多了一些 add()之類的方法,允許添加,刪除,設(shè)定元素, 還能向前或向后遍歷。   
實(shí)現(xiàn) List 接口的常用類有 LinkedList,ArrayList,Vector 和 Stack。

.
.
.

二.1.1、ArrayList

  • 查詢速度快,適合隨機(jī)訪問
  • 不同步

ArrayList 是一個(gè)動(dòng)態(tài)數(shù)組,也是我們最常用的集合。它允許任何符合規(guī)則的元素插入甚至·包括 null。每一個(gè) ArrayLst 都有一個(gè)初始容量(10),該容量代表了數(shù)組的大小。隨著容器中的元素不斷增加,容器的大小也會(huì)隨著增加。在每次向容器中增加元素的同時(shí)都會(huì)進(jìn)行容量檢查,當(dāng)快溢出時(shí),就會(huì)進(jìn)行擴(kuò)容操作。所以如果我們明確所插入元素的多少,最好指定一個(gè)初始容量值,避免過多的進(jìn)行擴(kuò)容操作而浪費(fèi)時(shí)間、效率

size、isEmpty、get、set、iterator 和 listIterator 操作都以固定時(shí)間運(yùn)行。add 操作以分?jǐn)偟墓潭〞r(shí)間運(yùn)行,也就是說,添加 n 個(gè)元素需要 O(n) 時(shí)間(由于要考慮到擴(kuò)容,所以這不只是添加元素會(huì)帶來分?jǐn)偣潭〞r(shí)間開銷那樣簡單)。

ArrayList 擅長于隨機(jī)訪問。同時(shí) ArrayList 是非同步的。

ArrayList一些自己的方法

  • void add(int index,Object e):將元素e添加到List集合中的index處;
  • boolean addAll(int index,Collection c):將集合c所包含的所有元素都插入在List集合的index處;
  • Object get(int index):返回集合index索引處的元素;
  • int indexOf(Object o):返回對象o在List集合中第一次出現(xiàn)位置的索引;
  • int lastIndexOf(object o):返回對象o在List集合中最后一次出現(xiàn)的位置索引;
  • Object remove(int index):刪除并返回index索引處的元素;
  • Object set(int index,Object e):把集合index處的元素替換為e對象,返回以前在指定位置的元素;
  • List subList(int fromIndex,int toIndex):返回從所有fromIndex(包括)到toIndex(不包括)處所有集合元素的子集合。

.
.
.

二.1.2、LinkedList

  • 增刪速度快,涉及增刪頻繁的數(shù)據(jù)
  • 非同步

LinkedList 實(shí)現(xiàn)了 List 接口,允許 null 元素。
ArrayList是一個(gè)動(dòng)態(tài)數(shù)組,
而 LinkedList 是一個(gè)雙向鏈表。

LinkedList 除了有 ArrayList 的基本操作方法外還額外提供了 get,remove,insert 方法在 LinkedList 的首部或尾部。

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

與 ArrayList 一樣,LinkedList 也是非同步的。如果多個(gè)線程同時(shí)訪問一個(gè) List,則必須自己實(shí)現(xiàn)訪問同步。一種解決方法是在創(chuàng)建 List 時(shí)構(gòu)造一個(gè)同步的 List:

List list = Collections.synchronizedList(new LinkedList(…));

.
.
.

二.1.3、Vector

  • 線程安全

與 ArrayList 相似,但是 Vector 是同步的。所以說 Vector 是線程安全的動(dòng)態(tài)數(shù)組。它的操作與 ArrayList 幾乎一樣。

這個(gè)用的少,基本都是ArrayList多
.
.
.

二.1.4、Stack

Stack 繼承自 Vector,實(shí)現(xiàn)一個(gè)后進(jìn)先出的堆棧。Stack 提供 5 個(gè)額外的方法使得 Vector 得以被當(dāng)作堆棧使用?;镜?push 和 pop 方法,還有 peek 方法得到棧頂?shù)脑?,empty 方法測試堆棧是否為空,search 方法檢測一個(gè)元素在堆棧中的位置。Stack 剛創(chuàng)建后是空棧。

三、Collection —— Set

1、Set 接口

Set最大的特性就是不允許在其中存放的元素是重復(fù)的。Set 可以被用來過濾在其他集合中存放的元素,從而得到一個(gè)沒有包含重復(fù)新的集合。

Set 是一種不包含重復(fù)的元素的 Collection,即任意的兩個(gè)元素 e1 和 e2 都有 e1.equals(e2)=false,Set 最多有一個(gè) null 元素

很明顯,Set 的構(gòu)造函數(shù)有一個(gè)約束條件,傳入的 Collection 參數(shù)不能包含重復(fù)的元素。

Set的實(shí)現(xiàn)原理是基于Map的。Set中很多實(shí)現(xiàn)類和Map中的一些實(shí)現(xiàn)類的使用上非常的相似。Map中的“鍵值對”,其中的 “鍵”是不能重復(fù)的。這個(gè)和Set中的元素不能重復(fù)一致,其實(shí)Set利用的就是Map中“鍵”不能重復(fù)的特性來實(shí)現(xiàn)的。

請注意:必須小心操作可變對象(Mutable Object)。如果一個(gè) Set 中的可變元素改變了自身狀態(tài)導(dǎo)致 Object.equals(Object)=true 將導(dǎo)致一些問題

Set的兩個(gè)重要方法

  • public boolean add(Object o) :如果set中不存在指定元素,則向set加入 ;

  • public boolean addAll(Collection c) :過濾集合中的重復(fù)元素,向set加入 【將c中所有元素添加到Set中,如果Set中已有某一元素,則不添加,因Set不允許有重復(fù)值】;

三.1.1 HashSet

對于 HashSet 而言,它是基于 HashMap 實(shí)現(xiàn)的,底層采用 HashMap 來保存元素,所以如果對 HashMap 比較熟悉了,那么學(xué)習(xí) HashSet 也是很輕松的。

HashSet的元素存放順序和添加進(jìn)去時(shí)候的順序沒有任何關(guān)系【HashSet類按照哈希算法來存取集合中的對象,存取速度比較快】【允許包含值為null的元素,但最多只能有一個(gè)null元素】;

HashSet的巧妙實(shí)現(xiàn):就是建立一個(gè)“鍵值對”,“鍵”就是我們要存入的對象,“值”則是一個(gè)常量。這樣可以確保, 我們所需要的存儲(chǔ)的信息之是“鍵”。而“鍵”在Map中是不能重復(fù)的,這就保證了我們存入Set中的所有的元素都不重復(fù)。而判斷是否添加元素成功,則是通 過判斷我們向Map中存入的“鍵值對”是否已經(jīng)存在,如果存在的話,那么返回值肯定是常量:PRESENT ,表示添加失敗。如果不存在,返回值就為null 表示添加成功。

利用Set去重Demo

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Set;

public class TestClass {
    public static void main(String[] args) {
        Set<String> nameSet = new HashSet<String>();
        nameSet.add("張三");
        nameSet.add("李四");
        nameSet.add("王五");
        nameSet.add("張三");

        // 輸出結(jié)果 張三李四王五
        for(String name : nameSet){
        System.out.print(name + "\t");
        }

        // List集合去除重復(fù)基礎(chǔ)數(shù)據(jù)
        List<String> nameList = new ArrayList<String>();
        nameList.add("張三");
        nameList.add("李四");
        nameList.add("王五");
        nameList.add("趙六");
        nameSet.addAll(nameList);

        // 輸出結(jié)果 張三李四王五趙六
        for(String name : nameSet){
        System.out.print(name + "\t");
        }

        // 去除編號(hào)和用戶名一樣的 對象,需要重寫 equals 方法 和 hashCode方法
        User admin = new User(1, "admin");
        User user = new User(2, "user");
        User user1 = new User(2, "user");
        User admin1 = new User(3, "admin");

        Set<User> userSet = new HashSet<User>();
        userSet.add(admin);
        userSet.add(user);
        userSet.add(admin1);
        userSet.add(user1);

        // 輸入結(jié)果 admin1admin3user2
        for(User u : userSet){
        System.out.print(u.username + u.id + "\t");
        }
        System.out.println(user.equals(null));
        }
        }

        class User{
        protected Integer id;
        protected String username;
        public User(Integer id, String username){
        this.id = id;
        this.username = username;
        }

        /**
         * 如果對象類型是User 的話 則返回true 去比較hashCode值
         */
        @Override
        public boolean equals(Object obj) {
        if(obj == null) return false;
        if(this == obj) return true;
        if(obj instanceof User){ 
        User user =(User)obj;
        //if(user.id = this.id) return true; // 只比較id
        // 比較id和username 一致時(shí)才返回true 之后再去比較 hashCode
        if(user.id == this.id && user.username.equals(this.username)) return true;
        }
        return false;
        }

        /**
         * 重寫hashcode 方法,返回的hashCode 不一樣才認(rèn)定為不同的對象
         */
        @Override
        public int hashCode() {
        //return id.hashCode(); // 只比較id,id一樣就不添加進(jìn)集合
        return id.hashCode() * username.hashCode();
        }
        
}

三1.2 TreeSet

以紅黑樹的形式存儲(chǔ)集合元素,使用元素的自然順序?qū)υ剡M(jìn)行排序。整體性能比HashSet低

HashSet 和 TreeSet ,HashSet的整體性能總比TreeSet好,特別是添加和查詢操作,只有當(dāng)一個(gè)保持排序的Set時(shí)才使用TreeSet。

TreeSet則是對Set中的元素進(jìn)行排序存放(TreeSet類實(shí)現(xiàn)了SortedSet接口,能夠?qū)现械膶ο筮M(jìn)行排序)。

三1.3 LinkedHashSet

保持元素的添加順序;
增刪快

四、Queue

隊(duì)列是一種特殊的線性表,它只允許在表的前端進(jìn)行刪除操作,而在表的后端進(jìn)行插入操作。
LinkedList類實(shí)現(xiàn)了Queue接口,因此我們可以把LinkedList當(dāng)成Queue來用。
(隊(duì)列是一種數(shù)據(jù)結(jié)構(gòu).它有兩個(gè)基本操作:在隊(duì)列尾部加人一個(gè)元素,和從隊(duì)列頭部移除一個(gè)元素)

Queue接口與List、Set同一級(jí)別,都是繼承了Collection接口。LinkedList實(shí)現(xiàn)了Queue接 口。Queue接口窄化了對LinkedList的方法的訪問權(quán)限(即在方法中的參數(shù)類型如果是Queue時(shí),就完全只能訪問Queue接口所定義的方法 了,而不能直接訪問 LinkedList的非Queue的方法),以使得只有恰當(dāng)?shù)姆椒ú趴梢允褂?。BlockingQueue 繼承了Queue接口。

demo

public class Main {
    public static void main(String[] args) {
        //add()和remove()方法在失敗的時(shí)候會(huì)拋出異常(不推薦)
        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()); //返回第一個(gè)元素,并在隊(duì)列中刪除
        for(String q : queue){
            System.out.println(q);
        }
        System.out.println("===");
        System.out.println("element="+queue.element()); //返回第一個(gè)元素 
        for(String q : queue){
            System.out.println(q);
        }
        System.out.println("===");
        System.out.println("peek="+queue.peek()); //返回第一個(gè)元素 
        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 提供了一個(gè)更通用的元素存儲(chǔ)方法。Map 集合類用于存儲(chǔ)元素對(稱作“鍵”和“值”),其中每個(gè)鍵映射到一個(gè)值。

Map的key不允許重復(fù),即同一個(gè)Map對象的任何兩個(gè)key通過equals方法比較總是返回false

如果使用自定義的類作為Map的key,應(yīng)重新該類的equals方法和compareTo方法時(shí)應(yīng)有一致的返回結(jié)果:即兩個(gè)key通過equals方法比較返回true時(shí),它們通過compareTo方法比較應(yīng)該返回0。

Map集合與Set集合元素的存儲(chǔ)形式很像,如Set接口下有HashSet、LinkedHashSet、SortedSet(接口)、TreeSet、EnumSet等實(shí)現(xiàn)類和子接口,而Map接口下則有HashMap、LinkedHashMap、SortedMap(接口)、TreeMap、EnumMap等實(shí)現(xiàn)類和子接口。
Map有時(shí)也稱為字典,或關(guān)聯(lián)數(shù)組。

Map中包括一個(gè)內(nèi)部類:Entry。該類封裝了一個(gè)key-value對,Entry包含三個(gè)方法:

Object getkey():返回該Entry里包含的key值。
Object getValue():返回該Entry里包含的value值。
Object setValue():設(shè)置該Entry里包含的value值,并返回新設(shè)置的value值。
可以把Map理解成一個(gè)特殊的Set,只是該Set里包含的集合元素是Entry對象,而不是普通對象。

  • HashMap:非線程安全,速度快,無序,適用于在Map中插入、刪除和定位元素。
  • TreeMap: 線程安全,速度慢,有序,適用于按自然順序或自定義順序遍歷鍵(key)。

五.1 Hashtable實(shí)現(xiàn)類

HashMap和Hashtable都是Map接口的實(shí)現(xiàn)類,Hashtable是一個(gè)古老的Map實(shí)現(xiàn)類,它從JDK1.0起就有,它包含兩個(gè)煩瑣的方法:elements()(類似于Map接口定義的values()方法)和keys()(類似于Map接口定義的keySet()方法),現(xiàn)在很少使用這兩種方法。

  • 同步,線程安全,如果多線程訪問同一個(gè)Map對象,使用Hashtable實(shí)現(xiàn)類更好。
  • 不允許null值,key和value都不可以

HashMap、Hashtable判斷兩個(gè)key相等的標(biāo)準(zhǔn)是:兩個(gè)key通過equasl方法比較返回ture,兩個(gè)key的hashCode值相等。

五.2 HashMap實(shí)現(xiàn)類

  • 線程不安全,速度快
  • HashMap可以使用null作為key或value。
    (由于HashMap里的可以不能重復(fù),所以HashMap里最多只有一對key-value值為null,但可以有無數(shù)多項(xiàng)key-value對的value為null。)
  • 適用于在Map中插入、刪除和定位元素

HashMap:基于哈希表實(shí)現(xiàn)。使用HashMap要求添加的鍵類明確定義了hashCode()和equals()[可以重寫hashCode()和equals()]

LinkedHashMap (Linked了,當(dāng)然就有序了)

HashMap有一個(gè)子類:LinkedHashMap,它也是雙向鏈表來維護(hù)key-value對的次序,該鏈表定義了迭代順序,該迭代順序與key-value對的插入順序保持一致。
LinkedHashMap可以避免對HashMap、Hashtable里的key-value對進(jìn)行排序(只要插入key-value對時(shí)保持順序即可)。同時(shí)又可避免使用TreeMap所增加的成本。

LinkedHashMap需要維護(hù)元素的插入順序,因此性能略低于HashMap的性能,但在迭代訪問Map里的全部元素時(shí)將有很好的性能,因?yàn)樗枣湵韥砭S護(hù)內(nèi)部順序。

五.3 TreeMap實(shí)現(xiàn)類

  • 線程安全,速度慢
  • 有序
  • 適用于按自然順序或自定義順序遍歷鍵(key)。

Map接口派生了一個(gè)SortedMap子接口,TreeMap為其實(shí)現(xiàn)類。類似TreeSet排序,TreeMap也是基于紅黑樹對TreeMap中所有key進(jìn)行排序,從而保證TreeMap中所有key-value對處于有序狀態(tài)。TreeMap兩種排序方法:

  • 自然排序:TreeMap的所有key必須實(shí)現(xiàn)Comparable接口,而且所有key應(yīng)該是同一個(gè)類的對象,否則將會(huì)拋出ClassCastExcepiton異常。
  • 定制排序:創(chuàng)建TreeMap時(shí),傳入一個(gè)Comparator對象,該對象負(fù)責(zé)對TreeMap中所有key進(jìn)行排序。采用定制排序時(shí)不要求Map的key實(shí)現(xiàn)Comparable接口。

TreeMap中判斷兩個(gè)key相等的標(biāo)準(zhǔn)也是兩個(gè)key通過equals比較返回true,而通過compareTo方法返回0,TreeMap即認(rèn)為這兩個(gè)key是相等的。


參考

Java中的Set集合類
java中queue的使用

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 本篇文章帶你從Java源碼深入解析關(guān)于Java容器的概念。 參考文獻(xiàn): Java容器相關(guān)知識(shí)全面總結(jié) Java官方...
    Tsy遠(yuǎn)閱讀 20,261評論 13 142
  • Collection ├List │├LinkedList │├ArrayList │└Vector │└Stac...
    AndyZX閱讀 964評論 0 1
  • 1.Java集合框架是什么?說出一些集合框架的優(yōu)點(diǎn)? 每種編程語言中都有集合,最初的Java版本包含幾種集合類:V...
    Oneisall_81a5閱讀 963評論 0 10
  • 概述 Java集合框架由Java類庫的一系列接口、抽象類以及具體實(shí)現(xiàn)類組成。我們這里所說的集合就是把一組對象組織到...
    absfree閱讀 1,425評論 0 10
  • 天堂里燈火通明 照耀秋日黃昏 寧靜而華美 這是一只幸福遺落的鞋子 時(shí)光陳舊 永遠(yuǎn)懷念著另一只 四面而來的風(fēng)擦洗她的...
    憂傷沒有傷口閱讀 270評論 0 0

友情鏈接更多精彩內(nèi)容