容器
很多時(shí)候,我們寫程序需要進(jìn)行批量的操作,比如說,新增一批學(xué)生列表.那么就需要有容器來裝下這10個(gè)對(duì)象。
Java提供了許多容器來裝對(duì)象,在JDK的java.util包下,編程中最常用的有:
-
List:有序列表,有序地存儲(chǔ)元素 -
Set:集合,存儲(chǔ)的元素不可重復(fù) -
Map:映射,建立key->value關(guān)系 -
Quene:隊(duì)列,先進(jìn)先出 -
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接口:

來看看這些接口分別代表的含義:
| 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();
}
}

