集合

原文鏈接http://zhhll.icu/2020/java%E5%9F%BA%E7%A1%80/%E9%9B%86%E5%90%88/java%E5%9F%BA%E7%A1%80%E4%B9%8B%E9%9B%86%E5%90%88/

有時(shí)候需要存儲(chǔ)一組數(shù)據(jù)押桃,之前使用數(shù)組伊脓,但是數(shù)組具有固定的容量,但是在寫程序時(shí)并不知道需要多少對(duì)象凤优,在java.util包下提供了一套完整的集合類,包含List蜈彼、Set筑辨、Queue、Map幸逆。java集合類都可以自動(dòng)的調(diào)整自己的大小棍辕。

再創(chuàng)建集合時(shí)暮现,經(jīng)常使用泛型,可以在編譯期防止將錯(cuò)誤的類型放入到集合中楚昭。

集合概念

集合分為兩個(gè)基本接口

  • 集合(Collection):一個(gè)獨(dú)立元素的序列栖袋,List必須已插入順序保存元素,Set不能包含重復(fù)元素抚太,Queue按照排隊(duì)規(guī)則來(lái)確定對(duì)象產(chǎn)生的順序(一般是插入順序)

  • 映射(Map):一組成對(duì)的"鍵值對(duì)"對(duì)象塘幅,允許使用鍵來(lái)查找值。map允許我們使用一個(gè)對(duì)象來(lái)查找另一個(gè)對(duì)象

    Arrays.asList()的輸出是一個(gè)List尿贫,但是底層實(shí)現(xiàn)是數(shù)組电媳,沒(méi)法調(diào)整大小。

    List<String> list = Arrays.asList("123","234");
    list.add("345");//java.lang.UnsupportedOperationException
    

List

存儲(chǔ)有序庆亡,可以重復(fù)的元素匾乓,相當(dāng)于動(dòng)態(tài)數(shù)組
集合中元素所在類要重寫equals方法

  • ArrayList
  • LinkedList
  • Vector

兩種類型的list

  • ArrayList:擅長(zhǎng)隨機(jī)訪問(wèn)元素,但在List中間插入和刪除元素時(shí)速度較慢

  • LinkedList:擅長(zhǎng)在List中間進(jìn)行插入和刪除操作身冀,提供了優(yōu)化的順序訪問(wèn)钝尸,對(duì)于隨機(jī)訪問(wèn)相對(duì)較慢

List特性

  • 允許插入重復(fù)元素
  • 允許插入多個(gè)null元素
  • List提供了ListIterator迭代器,可以提供雙向訪問(wèn)

ArrayList和Vector的異同點(diǎn)

相同點(diǎn)

  • 兩者都是基于索引的搂根,內(nèi)部使用數(shù)組

  • 兩者維護(hù)插入順序珍促,可以根據(jù)插入順序來(lái)獲取元素

  • ArrayList和Vector的迭代器實(shí)現(xiàn)都是fail-fast的

  • ArrayList和Vector兩者都允許null值,也可以使用索引值對(duì)元素進(jìn)行隨機(jī)訪問(wèn)

不同點(diǎn)

  • Vector是同步的剩愧,ArrayList不是猪叙,但是已過(guò)時(shí),使用CopyOnWriteArrayList
  • ArrayList比Vector快

LinkedList鏈表

LinkedList添加了一些方法仁卷,使其可以被用作棧穴翩,隊(duì)列和雙向隊(duì)列,方法差異

  • getFirst()和element()是相同的锦积,都是返回列表的頭部芒帕,而并不刪除它,如果list為空丰介,則拋出NoSuchElementException異常背蟆。peek()方法在列表為空是返回null

  • removeFirst()和remove()方法相同,刪除并返回列表頭部元素哮幢,在列表為空時(shí)返回NoSuchElementException異常带膀,poll()在列表為空時(shí)返回null

  • addFirst()在列表頭部插入一個(gè)元素

  • offer()和add()和addLast()相同,在列表尾部添加一個(gè)元素

  • removeLast()刪除并返回列表的最后一個(gè)元素

ArrayList和LinkedList的區(qū)別

  • ArrayList是由數(shù)組支持的基于索引的數(shù)據(jù)結(jié)構(gòu)橙垢,支持對(duì)元素的隨機(jī)訪問(wèn)垛叨,復(fù)雜度為O(1),但是LinkedList是基于鏈表的柜某,存儲(chǔ)一系列的節(jié)點(diǎn)數(shù)據(jù)嗽元,每個(gè)節(jié)點(diǎn)都與前一個(gè)節(jié)點(diǎn)和下一個(gè)節(jié)點(diǎn)相連敛纲。雖然存在使用索引獲取元素的方法,但是內(nèi)部實(shí)現(xiàn)是從起始點(diǎn)開(kāi)始遍歷的还棱,時(shí)間復(fù)雜度是O(n)
  • 與ArrayList相比载慈,在LinkedList中插入、添加和刪除一個(gè)元素會(huì)更快
  • LinkedList比ArrayList消耗更多內(nèi)存珍手,因?yàn)樾枰鎯?chǔ)前后節(jié)點(diǎn)的引用

迭代器Iterators

Iterator

Iterator接口提供了遍歷任何Collection的接口办铡,取代了java集合框架中的Enumeration,迭代器允許調(diào)用者在迭代過(guò)程中移除數(shù)據(jù)

iterator只能單向移動(dòng)
  • 使用iterator()方法使集合返回一個(gè)Iterator琳要。Iterator將準(zhǔn)備好返回序列中的第一個(gè)元素寡具。

  • 使用next()方法獲得序列中的下一個(gè)元素。

  • 使用hasNext()方法檢查序列中是否還有元素稚补。

  • 使用remove()方法將迭代器最近返回的那個(gè)元素刪除童叠。

Enumeration和iterator的區(qū)別

  • Enumeration的速度是Iterator的兩倍,使用內(nèi)存也少课幕,但是iterator更加安全厦坛,使得一個(gè)集合在遍歷時(shí),會(huì)阻止其他線程去修改集合乍惊,Iterator允許移除元素
  • Iterator支持fail-fast機(jī)制杜秸,而Enumeration不支持,Iterator遍歷時(shí)润绎,當(dāng)其他線程修改集合內(nèi)容時(shí)撬碟,迭代器會(huì)立馬感知到,引起快速失敗莉撇,拋出ConcurrentModificationException異常
  • Enumeration本身不支持同步呢蛤,只是在Vector和hashtable實(shí)現(xiàn)Enumeration時(shí),添加了同步

ListIterator

  • ListIterator是Iterator的子類型棍郎,只能由各種List類生成其障,
  • Iterator只能向前移動(dòng),ListIterator可以雙向移動(dòng)涂佃,可以生成迭代器在列表中指向位置的后一個(gè)和前一個(gè)元素的索引静秆。

堆棧stack

堆棧是后進(jìn)先出(LIFO),最后壓入(push)棧的元素巡李,第一個(gè)被彈出(pop)棧。

java1.0中有一個(gè)stack類扶认,但是設(shè)計(jì)的不好侨拦,Java6添加了ArrayDeque,其中包含了直接實(shí)現(xiàn)堆棧功能的方法

  • push()添加元素到棧底
  • peek()和pop()返回對(duì)象辐宾,peek()返回棧頂元素狱从,但不從棧頂刪除膨蛮,而pop()刪除并返回棧頂元素

Set

Set不保存重復(fù)的元素。查找是Set最重要的操作季研,選擇HashSet實(shí)現(xiàn)敞葛,針對(duì)快速查找進(jìn)行了優(yōu)化。

存儲(chǔ)無(wú)序与涡,不可重復(fù)
添加Set集合中的元素所在類要重寫equals和hashCode方法

無(wú)序性:指的是元素在底層存儲(chǔ)的位置是無(wú)序的

  • HashSet沒(méi)有順序惹谐,使用散列函數(shù),HashSet維護(hù)順序與TreeSet或LinkedHashSet不同驼卖,因?yàn)樗鼈儗?shí)現(xiàn)具有不同的元素存儲(chǔ)方式

  • LinkedHashSet 也使用了散列氨肌,使用了鏈表來(lái)維護(hù)元素的插入順序,結(jié)果將按元素的插入順序顯示酌畜。元素必須定義hashCode()和equals()方法怎囚,遍歷元素時(shí),會(huì)按照添加的進(jìn)去的順序

  • TreeSet將元素存儲(chǔ)在紅黑樹(shù)數(shù)據(jù)結(jié)構(gòu)桥胞,可以從Set中獲取有序序列恳守,其中元素必須實(shí)現(xiàn)Comparable接口

    要求添加進(jìn)TreeSet的必須是同一個(gè)類的
    兩種排序方式
    1)自然排序:添加的類要實(shí)現(xiàn)Comparable接口,重寫compareTo方法
    2)定制排序: 使用TreeSet(Comparator<? super E> comparator) 構(gòu)造器 重寫compare(T o1, T o2);方法

Map

鍵值
key不可重復(fù)贩虾,一個(gè)key-value組成一個(gè)entry

map的分類

HashMap專為快速訪問(wèn)而設(shè)計(jì)催烘,TreeMap保持鍵始終處于排序狀態(tài),沒(méi)有HashMap快整胃。LinkedHashMap按插入順序保存其元素颗圣,但使用散列提供快速訪問(wèn)的能力。

  • HashMap 基于哈希表的實(shí)現(xiàn)屁使。為插入和定位鍵值對(duì)提供了常數(shù)時(shí)間性能在岂。可以通過(guò)構(gòu)造方法調(diào)整性能蛮寂,這些構(gòu)造方法允許設(shè)置哈希表的容量和裝填因子蔽午。可以添加key為null酬蹋,value為null
  • LinkedHashMap 與HashMap類似及老,但是當(dāng)遍歷時(shí),可以按照插入順序或最近最少使用(LRU)順序獲取鍵值對(duì)范抓。只比HashMap略慢骄恶,一個(gè)例外是在迭代時(shí),由于其使用鏈表維護(hù)內(nèi)部順序匕垫,所以會(huì)更快些僧鲁,按照添加進(jìn)Map的順序遍歷
  • TreeMap 基于紅黑樹(shù)實(shí)現(xiàn),當(dāng)查看鍵或鍵值對(duì)時(shí),按排序順序(由Comparable或Comparator確定)寞秃。TreeMap的側(cè)重點(diǎn)在于按排序順序獲得結(jié)果斟叼。TreeMap是唯一使用subMap()方法的Map,返回紅黑樹(shù)的一部分春寿,按照key所在類的指定屬性進(jìn)行排序朗涩,要求key是同一個(gè)類的對(duì)象(同TreeSet)
  • WeakHashMap 一個(gè)具有弱鍵的Map,為了解決某些類型的問(wèn)題绑改,它允許釋放Map所引用的對(duì)象谢床。如果Map外沒(méi)有對(duì)特定鍵的引用,則可以對(duì)該鍵進(jìn)行垃圾回收
  • ConcurrentHashMap 不使用同步鎖定的線程安全Map
  • IdentityHashMap 使用==來(lái)比較鍵绢淀,僅用于解決特殊問(wèn)題
  • HashTable 不可添加key為null萤悴,value為null的 子類Properties 處理屬性文件

HashMap工作情況

HashMap在Map.Entry靜態(tài)內(nèi)部類實(shí)現(xiàn)存儲(chǔ)鍵值對(duì),HashMap使用哈希算法皆的,在put和get方法中覆履,使用hashCode和equals方法,使用put方法時(shí)费薄,使用key的hashcode和哈希算法來(lái)找出存儲(chǔ)鍵值對(duì)的索引硝全,Entry存儲(chǔ)在LinkedList中,如果存在entry楞抡,使用equals檢查傳遞的key是否存在伟众,如果存在,會(huì)覆蓋掉value召廷,如果不存在凳厢,會(huì)創(chuàng)建一個(gè)新的entry然后保存。get的時(shí)候也是先通過(guò)hashcode找到數(shù)組中的索引竞慢,然后使用equals找到正確的Entry先紫,在進(jìn)行取值

HashMap默認(rèn)初始容量是32,負(fù)載因子是0.75筹煮,閾值是容量乘以負(fù)載因子遮精,當(dāng)map的大小比閾值大時(shí),HashMap會(huì)對(duì)map的內(nèi)容進(jìn)行重新哈希败潦。

HashMap和HashTable的區(qū)別

  • HashMap允許key和value為null本冲,HashTable不允許
  • HashTable是同步的,HashMap不是
  • HashMap可以轉(zhuǎn)為L(zhǎng)inkedHashMap劫扒,使得遍歷有序檬洞,HashTable的順序無(wú)法預(yù)知
  • HashMap提供對(duì)key的set進(jìn)行遍歷,所以是fail-fast的沟饥,HashTable提供對(duì)key的Enumeration進(jìn)行遍歷疮胖,不支持fail-fast
  • HashTable應(yīng)該被CocurrentHashMap替代

隊(duì)列

隊(duì)列操作

隊(duì)列是一個(gè)先進(jìn)先出(FIFO)集合环戈,LinkedList實(shí)現(xiàn)了Queue接口,并且提供了一些方法支持隊(duì)列行為

  • offer()在隊(duì)列尾部插入一個(gè)元素

  • peek()和element()返回隊(duì)列頭而不刪除它澎灸,如果隊(duì)列為空,element()拋出NoSuchElementException遮晚,而peek()返回null

  • poll()和remove()都刪除并返回隊(duì)頭元素性昭,如果隊(duì)列為空,poll()返回null县遣,remove()拋出NoSuchElementException

PriorityQueue優(yōu)先級(jí)隊(duì)列

優(yōu)先級(jí)隊(duì)列聲明下一個(gè)彈出的元素是最需要的元素糜颠。

BlockingQueue隊(duì)列

是concurrent包下的類,在進(jìn)行檢索或移除一個(gè)元素的時(shí)候萧求,會(huì)等待隊(duì)列變成非空其兴;當(dāng)添加一個(gè)元素的時(shí)候,會(huì)等待隊(duì)列中的可用空間夸政。主要用于實(shí)現(xiàn)生產(chǎn)者-消費(fèi)者模式

Collections工具類

unmodifiableCollection方法

Collections.unmodifiableCollection(list)元旬;Collections.unmodifiableList(list);使用該方法會(huì)創(chuàng)建一個(gè)只讀集合守问,所有改變集合的操作都會(huì)拋出UnsupportedOperationException

public static <T> Collection<T> unmodifiableCollection(Collection<? extends T> c) {
        return new UnmodifiableCollection<>(c);
}

synchronizedCollection方法

Collections.synchronizedCollection(list)方法可以創(chuàng)建一個(gè)線程安全的集合

public static <T> Collection<T> synchronizedCollection(Collection<T> c) {
    return new SynchronizedCollection<>(c);
}

問(wèn)題

1匀归、遍歷時(shí)移除List中的元素

使用forEach和Iterator

在使用forEach遍歷時(shí),實(shí)際上是使用的Iterator耗帕,使用的核心方法是hasNext()和next()穆端,但是使用的是list.remove,來(lái)看個(gè)例子

//源碼
public class TestList {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("J");
        list.add("A");
        list.add("V");
        list.add("A");
        for (String s: list) {
            list.remove(s);
        }
    }
}

//編譯之后
public class TestList {
    public TestList() {
    }

    public static void main(String[] args) {
        List<String> list = new ArrayList();
        list.add("J");
        list.add("A");
        list.add("V");
        list.add("A");
        Iterator var2 = list.iterator();
        while(var2.hasNext()) {
            String s = (String)var2.next();
            list.remove(s);
        }
    }
}  

之前說(shuō)過(guò)仿便,Iterator在遍歷時(shí)体啰,不允許其他線程對(duì)該集合進(jìn)行操作,看一下ArrayList的iterator是怎么實(shí)現(xiàn)的

public E next() {
    checkForComodification();
    int i = cursor;
    if (i >= size)
        throw new NoSuchElementException();
    Object[] elementData = ArrayList.this.elementData;
    if (i >= elementData.length)
        throw new ConcurrentModificationException();
    cursor = i + 1;
    return (E) elementData[lastRet = i];
}

final void checkForComodification() {
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}

在每次獲取下一個(gè)元素時(shí)嗽仪,都會(huì)比較modCount 和 expectedModCount

然后在調(diào)用的list的remove方法會(huì)導(dǎo)致modCount增加(modCount表示被修改次數(shù))

public E remove(int index) {
        rangeCheck(index);

        modCount++;
        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }

此時(shí)iterator的next方法中兩個(gè)變量就不一致了荒勇,就會(huì)拋出ConcurrentModificationException異常

再看一下如果使用iterator的remove方法

public void remove() {
    if (lastRet < 0)
        throw new IllegalStateException();
    checkForComodification();

    try {
        ArrayList.this.remove(lastRet);
        cursor = lastRet;
        lastRet = -1;
        expectedModCount = modCount;
    } catch (IndexOutOfBoundsException ex) {
        throw new ConcurrentModificationException();
    }
}

iterator在remove之后會(huì)將modCount的值賦給expectedModCount,就不會(huì)出現(xiàn)兩個(gè)變量不等的情況了

不使用forEach遍歷

使用普通for循環(huán)钦幔,有兩種方式枕屉,第一種是使用正序遍歷,但是進(jìn)行remove操作之后要把遍歷的索引進(jìn)行修正減一鲤氢,否則在移除下一個(gè)的時(shí)候就會(huì)出錯(cuò)搀擂,第二種就是使用倒序遍歷

// 正序遍歷
for (int i = 0; i < list.size(); i++) {
    String s = list.remove(i);
    i = i - 1;
    System.out.println(s);
}

//倒序遍歷
for (int i = list.size() - 1; i >= 0; i--) {
    String s = list.remove(i);
    System.out.println(s);
}

2、fail-fast和fail-safe

java.util包中集合類被設(shè)計(jì)為fail-fast的卷玉,而java.util.concurrent中集合為fail-safe的哨颂。fail-fast迭代器拋出ConcurrentModificationException,而fail-safe迭代器從不拋出ConcurrentModificationException

3相种、Arrays.asList

這個(gè)方法返回的是一個(gè)ArrayList威恼,不過(guò)這個(gè)ArrayList是Arrays類的內(nèi)部類,在調(diào)用add方法的時(shí)候會(huì)直接報(bào)錯(cuò)

UnsupportedOperationException這是運(yùn)行時(shí)異常

public void add(int index, E element) {
    throw new UnsupportedOperationException();
}

由于本身的博客百度沒(méi)有收錄,博客地址http://zhhll.icu

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市惦界,隨后出現(xiàn)的幾起案子谭贪,更是在濱河造成了極大的恐慌,老刑警劉巖植酥,帶你破解...
    沈念sama閱讀 221,635評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異弦牡,居然都是意外死亡友驮,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門驾锰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)卸留,“玉大人,你說(shuō)我怎么就攤上這事椭豫〕苌” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 168,083評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵捻悯,是天一觀的道長(zhǎng)匆赃。 經(jīng)常有香客問(wèn)我,道長(zhǎng)今缚,這世上最難降的妖魔是什么算柳? 我笑而不...
    開(kāi)封第一講書人閱讀 59,640評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮姓言,結(jié)果婚禮上瞬项,老公的妹妹穿的比我還像新娘。我一直安慰自己何荚,他們只是感情好囱淋,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著餐塘,像睡著了一般妥衣。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上戒傻,一...
    開(kāi)封第一講書人閱讀 52,262評(píng)論 1 308
  • 那天税手,我揣著相機(jī)與錄音,去河邊找鬼需纳。 笑死芦倒,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的不翩。 我是一名探鬼主播兵扬,決...
    沈念sama閱讀 40,833評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼麻裳,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了器钟?” 一聲冷哼從身側(cè)響起津坑,我...
    開(kāi)封第一講書人閱讀 39,736評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎傲霸,沒(méi)想到半個(gè)月后国瓮,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,280評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡狞谱,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了禁漓。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片跟衅。...
    茶點(diǎn)故事閱讀 40,503評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖播歼,靈堂內(nèi)的尸體忽然破棺而出伶跷,到底是詐尸還是另有隱情,我是刑警寧澤秘狞,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布叭莫,位于F島的核電站,受9級(jí)特大地震影響烁试,放射性物質(zhì)發(fā)生泄漏雇初。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評(píng)論 3 333
  • 文/蒙蒙 一减响、第九天 我趴在偏房一處隱蔽的房頂上張望靖诗。 院中可真熱鬧,春花似錦支示、人聲如沸刊橘。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,340評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)促绵。三九已至,卻和暖如春嘴纺,著一層夾襖步出監(jiān)牢的瞬間败晴,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,460評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工颖医, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留位衩,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,909評(píng)論 3 376
  • 正文 我出身青樓熔萧,卻偏偏與公主長(zhǎng)得像糖驴,于是被迫代替她去往敵國(guó)和親僚祷。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容