[Java]重學(xué)Java-集合

容器

很多時(shí)候,我們寫程序需要進(jìn)行批量的操作,比如說,新增一批學(xué)生列表.那么就需要有容器來裝下這10個(gè)對(duì)象。
Java提供了許多容器來裝對(duì)象,在JDK的java.util包下,編程中最常用的有:

  1. List: 有序列表,有序地存儲(chǔ)元素
  2. Set: 集合,存儲(chǔ)的元素不可重復(fù)
  3. Map: 映射,建立key->value關(guān)系
  4. Quene: 隊(duì)列,先進(jìn)先出
  5. Stack: 棧,先進(jìn)后出

泛型

使用Java的集合類最好配合泛型進(jìn)行使用,以免發(fā)生以下的錯(cuò)誤:

package com.tea.modules.java8.generic;

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

/**
 * com.tea.modules.java8.generic <br>
 * 不要使用原始類型
 * @author jaymin
 * @since 2021/6/4
 */
@SuppressWarnings("all")
public class DoNotUseRawType {
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add(1);
        list.add("2");
    }
}

例子中,我們對(duì)list沒做任何類型限制,添加了數(shù)字1和字符串2,那么在遍歷取值使用的時(shí)候,我們可能會(huì)將字符相關(guān)的操作用在數(shù)字上,從而引發(fā)了異常。

equals 和 hashcode

如果存儲(chǔ)的是對(duì)象,那么需要重寫equals和hashcode,否則你的集合操作會(huì)產(chǎn)生意外的異常.

package com.tea.modules.java8.collection.prevent;

import lombok.extern.slf4j.Slf4j;

import java.util.*;

/**
 * com.xjm.collection.prevent<br>
 * 對(duì)集合對(duì)象進(jìn)行操作,最好重寫equals和hashcode,避免帶來不好的影響<br>
 *
 * @author xiejiemin
 * @create 2020/12/25
 */
@Slf4j
public class EqualsAndHashCode {

    public static class Student implements Comparable<Student>{
        /**
         * 姓名
         */
        private String name;
        /**
         * 性別:0-man|1-women
         */
        private Integer gender;

        public Student(String name, Integer gender) {
            this.name = name;
            this.gender = gender;
        }

        public Student() {
        }

        public String getName() {
            return name;
        }

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

        public Integer getGender() {
            return gender;
        }

        public void setGender(Integer gender) {
            this.gender = gender;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj instanceof Student) {
                Student student = (Student) obj;
                return Objects.equals(this.name, student.name);
            }
            return false;
        }

        @Override
        public int hashCode() {
            int result = name.hashCode();
            result = 31 * result + gender;
            return result;
        }

        @Override
        public int compareTo(Student student) {
            return this.gender - student.gender;
        }
    }

    /**
     * <h2>實(shí)現(xiàn)/不實(shí)現(xiàn)equals和hashcode對(duì)于判等的影響</h2>
     */
    private static void beforeOverrideEqualsAndHashCode() {
        Student jackA = new Student("Jack", 20);
        Student jackB = new Student("Jack", 20);
        log.info("Before override equals and hashcode->{}", Objects.equals(jackA,jackB));
        Set<Student> studentSet = new HashSet<>();
        studentSet.add(jackA);
        studentSet.add(jackB);
        studentSet.forEach(System.out::println);
        Map<Student,Integer> studentMap = new HashMap<>();
        studentMap.put(jackA,20);
        studentMap.put(jackB,20);
        System.out.println(studentMap.size());
    }

    /**
     * <h3>類實(shí)現(xiàn)了compareTo方法,就需要實(shí)現(xiàn)equals方法</h3>
     * <h3>compareTo與equals的實(shí)現(xiàn)過程需要同步</h3>
     */
    private static void compareToWithEquals(){
        ArrayList<Student> students = new ArrayList<>();
        students.add(new Student("Jack",10));
        students.add(new Student("Jack",20));

        Student jack = new Student("Jack", 20);
        // equals
        int studentIndex = students.indexOf(jack);
        // compareTo
        int index = Collections.binarySearch(students, jack);

        System.out.println(studentIndex+"   "+index);
    }
    public static void main(String[] args) {
        beforeOverrideEqualsAndHashCode();
        compareToWithEquals();
    }
}

接口與實(shí)現(xiàn)

以列表為例子,我們通常這樣使用:

List<String> strings = new ArrayList<>();

List是列表這個(gè)數(shù)據(jù)結(jié)構(gòu)的頂層接口,ArrayList是具體的實(shí)現(xiàn),ArrayList底層基于數(shù)組實(shí)現(xiàn),可以動(dòng)態(tài)擴(kuò)容。
如果此時(shí),我們需要更換隊(duì)列的實(shí)現(xiàn)類,那么直接更換即可,依然返回List接口類型的結(jié)果.

List<String> strings = new LinkedList<>();

Collection

java中的絕大多數(shù)集合類都實(shí)現(xiàn)了Collection接口:

collection

來看看這些接口分別代表的含義:

method description
size 返回集合中元素的個(gè)數(shù)
isEmpty 判斷當(dāng)前集合中是否存儲(chǔ)了元素
contains 集合中是否包含某個(gè)對(duì)象
iterator 獲取迭代器
toArray 將集合轉(zhuǎn)換成Object數(shù)組
toArray(T[]) 將集合轉(zhuǎn)換成對(duì)象數(shù)組
add 往集合中添加元素
remove 從集合中移除元素
containsAll 集合中是否有子集
addAll 將兩個(gè)集合合并
removeAll 求集合的差集
removeIf 如果符合Predicate的條件,將元素從集合中刪除
retainAll 從該集合中移除所有未包含在指定集合中的元素
clear 移除集合中所有元素
stream 將集合轉(zhuǎn)為流
parallerStream 將集合轉(zhuǎn)為并行流

Iterator-迭代器

關(guān)于迭代器,可以看看我之前的文章,這里不重復(fù)描述
點(diǎn)我前往

  • 關(guān)于為什么要設(shè)計(jì)迭代器的解釋

要使用集合,必須對(duì)集合的確切類型編程。這一開始可能看起來不是很糟糕,但是考慮下面的情況:如果原本是對(duì) List 編碼的,但是后來發(fā)現(xiàn)如果能夠?qū)⑾嗤拇a應(yīng)用于 Set 會(huì)更方便,此時(shí)應(yīng)該怎么做?或者假設(shè)想從一開始就編寫一段通用代碼,它不知道或不關(guān)心它正在使用什么類型的集合,因此它可以用于不同類型的集合,那么如何才能不重寫代碼就可以應(yīng)用于不同類型的集合?
迭代器(也是一種設(shè)計(jì)模式)的概念實(shí)現(xiàn)了這種抽象。迭代器是一個(gè)對(duì)象,它在一個(gè)序列中移動(dòng)并選擇該序列中的每個(gè)對(duì)象,而客戶端程序員不知道或不關(guān)心該序列的底層結(jié)構(gòu)。

摘自《Java編程思想》

集合實(shí)現(xiàn)

  • 隊(duì)列與集合

    abstractCollection

  • 映射

    map

說到映射,很多朋友可能覺得很迷糊,其實(shí)就是key->value的關(guān)系.

package com.tea.modules.java8.collection.maps;

import java.util.HashMap;
import java.util.Map;

/**
 * com.tea.modules.java8.collection.maps <br>
 * 用于測(cè)試Map的API
 * @author jaymin
 * @since 2021/9/18
 */
public class WhatIsMap {

    public static void main(String[] args) {
        Map<String,Object> hashMap = new HashMap<>();
        hashMap.put("男","man");
        hashMap.put("女","woman");
        System.out.println(hashMap.get("男"));
    }
}
collection description
ArrayList 可以動(dòng)態(tài)擴(kuò)容和縮減的有序列表
LinkedList 可以在任一位置進(jìn)行插入和刪除的有序列表,基于鏈表
HashSet 無重復(fù)元素的無序集合
HashMap 存儲(chǔ)鍵值對(duì)的映射表,無序
EnumSet 枚舉集合
LinkedHashSet 記錄插入順序的無重復(fù)元素集合
PriorityQueue 一種允許高效刪除最小元素的集合
TreeMap Key根據(jù)自然排序的映射表
LinkedHashMap 記錄插入順序的映射表
WeakHashMap 對(duì)象引用為弱引用的映射表
IdentityHashMap 用==來對(duì)比鍵值的映射表

快速失敗機(jī)制

package com.tea.modules.java8.collection.prevent;

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

/**
 * com.tea.modules.java8.collection.prevent <br>
 * 觸發(fā)集合的ConcurrentModificationException <br>
 * 可以想象, 如果在某個(gè)迭代器修改集合時(shí), 另一個(gè)迭代器對(duì)其進(jìn)行遍歷,一定會(huì)出現(xiàn)
 * 混亂的狀況。例如,一個(gè)迭代器指向另一個(gè)迭代器剛剛刪除的元素前面,現(xiàn)在這個(gè)迭代器
 * 就是無效的,并且不應(yīng)該再使用。鏈表迭代器的設(shè)計(jì)使它能夠檢測(cè)到這種修改。如果迭代
 * 器發(fā)現(xiàn)它的集合被另一個(gè)迭代器修改了, 或是被該集合自身的方法修改了, 就會(huì)拋出一個(gè)
 * ConcurrentModificationException 異常 <br>
 * JDK文檔:   <br>
 * 例如,通常不允許一個(gè)線程修改一個(gè)集合,而另一個(gè)線程正在對(duì)其進(jìn)行迭代。  <br>
 * 通常,在這些情況下迭代的結(jié)果是不確定的。   <br>
 * 如果檢測(cè)到此行為,某些迭代器實(shí)現(xiàn)(包括 JRE 提供的所有通用集合實(shí)現(xiàn)的實(shí)現(xiàn))可能會(huì)選擇拋出此異常。   <br>
 * 這樣做的迭代器被稱為快速失敗迭代器,因?yàn)樗鼈兛焖俣蓛舻厥。? <br>
 * 而不是冒著在未來不確定的時(shí)間出現(xiàn)任意、非確定性行為的風(fēng)險(xiǎn)。<br>
 * @author jaymin
 * @since 2021/9/18
 */
public class ConcurrentModificationExceptionDemo {

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("A");
        list.add("B");
        ListIterator<String> stringListIteratorA = list.listIterator();
        ListIterator<String> stringListIteratorB = list.listIterator();
        stringListIteratorA.next();
        stringListIteratorA.remove();
        stringListIteratorB.next();
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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