java集合超詳細(xì)(最新推薦)
1 - 概述
所有的集合類和集合接口都在java.util包下。
在內(nèi)存中申請一塊空間用來存儲數(shù)據(jù),在Java中集合就是替換掉定長的數(shù)組的一種引用數(shù)據(jù)類型。
2 - 集合與數(shù)組的區(qū)別
長度區(qū)別
數(shù)組長度固定,定義長了造成內(nèi)存空間的浪費,定義短了不夠用。
集合大小可以變,用多少空間拿多少空間。
內(nèi)容區(qū)別
數(shù)組可以存儲基本數(shù)據(jù)類型和引用數(shù)據(jù)類型
集合中能存儲引用數(shù)據(jù)類型(存儲的為對象的內(nèi)存地址)
list.add(100);//為自動裝箱,100為Integer包裝的
元素區(qū)別
數(shù)組中只能存儲同一種類型成員
集合中可以存儲不同類型數(shù)據(jù)(一般情況下也只存儲同一種類型的數(shù)據(jù))
集合結(jié)構(gòu)
在java中每一個不同的集合,底層會對應(yīng)不同的數(shù)據(jù)結(jié)構(gòu)。往不同的集合中
存儲元素,等于將數(shù)據(jù)放到了不同的數(shù)據(jù)結(jié)構(gòu)當(dāng)中。什么是數(shù)據(jù)結(jié)構(gòu)?數(shù)據(jù)存儲的
結(jié)構(gòu)就是數(shù)據(jù)結(jié)構(gòu)。不同的數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)存儲方式不同。
- 單列集合 Collection
- List可以重復(fù):ArrayList/LinkedList
- Set不可重復(fù):HashSet/TreeSet

(大量文字插入會導(dǎo)致圖片不清,所以在此進(jìn)行更詳細(xì)的描述)
- List特點:此處順序并不是大小順序,而是存入數(shù)據(jù)的先后順序。有序因為List集合都有下標(biāo),下標(biāo)從0開始,以遞增。
- Set特點:取出順序不一定為存入順序,另外Set集合沒有下標(biāo)。
- ArrayList是非線程安全的。
- HashSet集合在new的時候,底層實際上new了一個HashMap集合。向HashSet集合中存儲元素,實際上是存儲到了HashMap的key中了。HashMap集合是一個Hash表數(shù)據(jù)結(jié)構(gòu)。
- SortedSet集合存儲元素的特點:由于繼承了Set集合,所以他的特點也是無序不可重復(fù),但是放在SortedSet集合中的元素可以自動排序。放到該集合中的元素是自動按照大小順序排序的。
- TreeSet集合底層實際上是TreeMap。TreeSet集合在new的時候,底層實際上new了一個TreeMap集合。向TreeSet集合中存儲元素,實際上是存儲到了TreeMap的key中了。TreeMap集合是一個二叉樹數(shù)據(jù)結(jié)構(gòu)。
雙列集合Map:HashMap/TreeMap

粗體是接口 斜體是實現(xiàn)類
3- Collection集合
3.1 - 概述
單列集合的頂層接口,既然是接口就不能直接使用,需要通過實現(xiàn)類!~
3.2 - Collection集合的的常用方法
| 方法名 | 說明 |
|---|---|
boolean add(E e) | 添加元素到集合的末尾(追加) |
boolean remove(Object o) | 刪除指定的元素,成功則返回true(底層調(diào)用equles) |
void clear() | 清空集合 |
boolean contains(Object o) | 判斷元素在集合中是否存在,存在則返回true(底層調(diào)用equles) |
boolean isEmpty() | 判斷集合是否為空,空則返回true |
int size() | 返回集合中元素個數(shù) |
import java.util.ArrayList;
import java.util.Collection;
/**
* @author Mr.樂
* @Description
*/
public class Collection_01 {
public static void main(String[] args) {
//父類的引用指向子類的對象,形成多態(tài)
Collection<String> con = new ArrayList<>();
//追加的方式添加元素
con.add("東邪");
con.add("西毒");
con.add("南帝");
con.add("北丐");
con.add("中神通");
//刪除,通過元素名稱刪除元素
System.out.println(con.remove("西毒"));
//判斷集合中是否包含指定參數(shù)元素
System.out.println(con.contains("西毒")); //false
System.out.println(con.contains("東邪")); //true
//獲取集合中元素個數(shù)
System.out.println(con.size());
//判斷是否為空
System.out.println(con.isEmpty());//false
//清空集合
con.clear();
//判斷是否為空
System.out.println(con.isEmpty());//true
System.out.println(con);//打印集合的元素
}
}3.3 - Collection集合的遍歷
以下迭代方式,是所有Collection通用的一種方式。在Map集合中不能使用,在所有的Collection以及子類中使用。


import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
/**
* @author Mr.樂
* @Description Collection 集合的遍歷
*/
public class Connection_02 {
public static void main(String[] args) {
//多態(tài)
Collection<String> con = new ArrayList<>();
//添加元素
con.add("abc");
con.add("def");
con.add("100");
con.add("444");
//Collection集合的遍歷方式
//因為沒有索引的概念,所以Collection集合不能使用fori進(jìn)行遍歷
//增強版for循環(huán),其實底層使用的也是迭代器,在字節(jié)碼文件中查看
for (String str : con) {
System.out.print(str + "\t");
}
System.out.println();//換行
//迭代器,集合專屬的遍歷工具
Iterator<String> it = con.iterator();//創(chuàng)建迭代器對象
while (it.hasNext()){//判斷下一個位置是否有元素
System.out.print(it.next() + "\t");//獲取到下一個位置的元素
}
}
}3.4-Iterator的remove
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
/**
* @author Mr.樂
* @Description
*/
public class Connection_remove {
public static void main(String[] args) {
// 創(chuàng)建集合
Collection c = new ArrayList();
// 注意:此時獲取的迭代器,指向的是那是集合中沒有元素狀態(tài)下的迭代器。
// 一定要注意:集合結(jié)構(gòu)只要發(fā)生改變,迭代器必須重新獲取。
// 當(dāng)集合結(jié)構(gòu)發(fā)生了改變,迭代器沒有重新獲取時,調(diào)用next()方法時:java.util.ConcurrentModificationException
Iterator it = c.iterator();
// 添加元素
c.add(1); // Integer類型
c.add(2);
c.add(3);
// 獲取迭代器
//Iterator it = c.iterator();
/*while(it.hasNext()){
// 編寫代碼時next()方法返回值類型必須是Object。
// Integer i = it.next();
Object obj = it.next();
System.out.println(obj);
}*/
Collection c2 = new ArrayList();
c2.add("abc");
c2.add("def");
c2.add("xyz");
Iterator it2 = c2.iterator();
while(it2.hasNext()){
Object o = it2.next();
// 刪除元素
// 刪除元素之后,集合的結(jié)構(gòu)發(fā)生了變化,應(yīng)該重新去獲取迭代器
// 但是,循環(huán)下一次的時候并沒有重新獲取迭代器,所以會出現(xiàn)異常:java.util.ConcurrentModificationException
// 出異常根本原因是:集合中元素刪除了,但是沒有更新迭代器(迭代器不知道集合變化了)
//c2.remove(o); // 直接通過集合去刪除元素,沒有通知迭代器。(導(dǎo)致迭代器的快照和原集合狀態(tài)不同。)
// 使用迭代器來刪除可以嗎?
// 迭代器去刪除時,會自動更新迭代器,并且更新集合(刪除集合中的元素)。
it2.remove(); // 刪除的一定是迭代器指向的當(dāng)前元素。
System.out.println(o);
}
System.out.println(c2.size()); //0
}
}4-List
原型ArrayList<E>
- ArrayList是一個List接口的實現(xiàn)類,底層使用的是一個可以調(diào)整大小的數(shù)組實現(xiàn)的。
<E>:是一種特殊的數(shù)據(jù)類型(引用數(shù)據(jù)類型) -- 泛型- ArrayList<String> 或者 ArrayList<Integer> 或者 ArrayList<Student>
4.1 - ArrayList構(gòu)造和添加方法
| 方法名 | 說明 |
|---|---|
public ArrayList<E>() | 創(chuàng)建一個空集合 |
public boolean add(E e) | 將指定的參數(shù)元素追加到集合的末尾 |
public void add(int index ,E e) | 在集合的指定位置添加指定的元素(插入元素) |
public void addAll(E object) | 用于將指定集合中所有元素添加到當(dāng)前集合中 |
/**
* @author Mr.樂
* @Description ArrayList構(gòu)造和添加方法
*/
public class ArrayList_01 {
public static void main(String[] args) {
//創(chuàng)建空集合
ArrayList<String> list = new ArrayList<>();//泛型定義為String
//采用默認(rèn)追加的方式添加元素
System.out.println(list.add("劉德華"));
System.out.println(list.add("張學(xué)友"));
System.out.println(list.add("郭富城"));
System.out.println(list.add("黎明"));
//插入的方式添加元素
// list.add(10,"譚詠麟");//插入元素方法索引值不能大于集合中元素個數(shù)
// list.add(4,"譚詠麟");//表示在集合中最后位置插入元素,與追加相同
list.add(1,"譚詠麟");//指定位置插入元素,索引位置之后的元素會自動向后進(jìn)行移動
ArrayList<String> newList = new ArrayList<>();//創(chuàng)建新的集合
newList.add("小沈陽");
newList.add("宋小寶");
newList.add("趙四");
newList.add("劉能");
//查看集合中的元素
System.out.println("原集合內(nèi)部元素:" + list);
System.out.println("新集合內(nèi)部元素:" + newList);
list.addAll(newList); //將新集合全部元素添加到原集合中
System.out.println("原集合內(nèi)部元素:" + list);
}
}4.2 - ArrayList集合常用方法
| 方法名 | 說明 |
|---|---|
public boolean remove(Object o) | 刪除指定的元素,成功則返回true |
public E remove(int index) | 刪除指定索引位置的元素,返回被刪除的元素 |
public E set(int index,E e) | 修改指定索引位置的元素,返回修改前的元素 |
public E get(int index) | 獲取指定索引對應(yīng)的元素 |
public int size() | 獲取結(jié)合中元素個數(shù) |
import java.util.ArrayList;
import java.util.Iterator;
/**
* @author Mr.樂
* @Description ArrayList集合常用方法
*/
public class ArrayList_02 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
//追加方式添加元素
list.add("東邪");
list.add("西毒");
list.add("南帝");
list.add("北丐");
list.add("中神通");
//刪除
System.out.println(list.remove("西毒"));//通過元素名稱刪除,返回boolean
System.out.println(list.remove(1));//通過索引刪除元素,返回被刪除元素名
//修改
System.out.println(list.set(1,"西毒"));//指定索引位置修改元素,并返回被修改元素
System.out.println("原集合中元素有:" + list);
//獲取方法
System.out.println(list.get(1));//通過指定索引位置獲取集合元素
//獲取集合元素個數(shù)
System.out.println(list.size());
//集合的遍歷,普通for循環(huán)
for (int i = 0; i < list.size(); i++) {
System.out.print(list.get(i) + "\t");
}
System.out.println();
//增強版for循環(huán)
for (String name : list) {
System.out.print(name+ "\t");
}
System.out.println();
//迭代器
Iterator<String> it = list.iterator();//創(chuàng)建迭代器
while (it.hasNext()){//判斷下一個位置是否有元素
System.out.print(it.next() + "\t"); //next方法表示獲取下一個位置的元素
}
System.out.println();
//Stream流
list.stream().forEach(System.out::println);
}
}4.3 -ArrayList實現(xiàn)原理
底層代碼:
屬性:
DEFAULT_CAPACITY = 10 默認(rèn)長度,初始化容量為10Object[] EMPTY_ELEMENTDATA = {} //有參構(gòu)造所創(chuàng)建Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {} //無參構(gòu)造所創(chuàng)建的Object[] elementData;底層為Object類型的數(shù)組,存儲的元素都在此。int size 實際存放的個數(shù)
構(gòu)造方法 :
//一個參數(shù)的構(gòu)造
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
//參數(shù)如果大于零,則為創(chuàng)建數(shù)組的長度;
//參數(shù)如果等于零,EMPTY_ELEMENTDATA;
//參數(shù)如果小于0,拋出異常。
//無參構(gòu)造
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//DEFAULTCAPACITY_EMPTY_ELEMENTDATA new對象時默認(rèn)為0 當(dāng)添加第一個元素的時候,數(shù)組擴容至10add方法源碼:(jdk1.8與之不同,此處為jdk16)
//源碼
public boolean add(E e) {
modCount++;//操作次數(shù)
add(e, elementData, size);
//e 操作對象; elementData 底層操作的數(shù)組;size 默認(rèn)大小0
return true;
}
------------------------------------------------
private void add(E e, Object[] elementData, int s) {
if (s == elementData.length)//ture
elementData = grow();
elementData[s] = e; //存數(shù)據(jù)
size = s + 1; //最小需要長度
}
----------------------------------------------------------
private Object[] grow() {
return grow(size + 1);
}
-----------------------------------------------------
private Object[] grow(int minCapacity) { //初始傳入為size+1 為1
int oldCapacity = elementData.length; //初始為0
if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//if條件為初始數(shù)組長度>0或者數(shù)組不是無參構(gòu)造構(gòu)建的
int newCapacity = ArraysSupport.newLength(oldCapacity, //舊數(shù)組的長度
minCapacity - oldCapacity, /* minimum growth */
//最小需要長度-舊數(shù)組的長度 大于0代表空間不足
oldCapacity >> 1 /* preferred growth */);
//二進(jìn)制位右移1位 位舊數(shù)組長度/2
return elementData = Arrays.copyOf(elementData, newCapacity);
將數(shù)據(jù)放入新數(shù)組中
} else {
return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
//數(shù)組長度 DEFAULT_CAPACITY為10 此處代表無參構(gòu)造默認(rèn)長度為10
}
}
----------------------------------------------------
public static int newLength(int oldLength, int minGrowth, int prefGrowth) {
// assert oldLength >= 0
// assert minGrowth > 0
int newLength = Math.max(minGrowth, prefGrowth) + oldLength;
//如果prefGrowth>minGrowth 擴容1.5倍 minGrowth>prefGrowth為需要多少給多少
if (newLength - MAX_ARRAY_LENGTH <= 0) {
//MAX_ARRAY_LENGTH為int最大值 表示新數(shù)組長度如果小于int的最大值
return newLength;
}
return hugeLength(oldLength, minGrowth);
//返回int最大值
}ArrayList集合底層是數(shù)組,怎么優(yōu)化?
盡可能少的擴容。因為數(shù)組擴容效率比較低,建議在使用ArrayList集合 的時候預(yù)估計元素的個數(shù),給定一個初始化容量。數(shù)組優(yōu)點:
檢索效率比較高。(每個元素占用空間大小相同,內(nèi)存地址是連續(xù)的,知道首元素內(nèi)存地址,
然后知道下標(biāo),通過數(shù)學(xué)表達(dá)式計算出元素的內(nèi)存地址,所以檢索效率最高。)數(shù)組缺點:
隨機增刪元素效率比較低。
另外數(shù)組無法存儲大數(shù)據(jù)量。(很難找到一塊非常巨大的連續(xù)的內(nèi)存空間。)向數(shù)組末尾添加元素,效率很高,不受影響。
4.4 -LinkedList實現(xiàn)原理
底層代碼
屬性:
transient int size = 0;//初始長度
transient Node<E> first;//頭節(jié)點
transient Node<E> last;//尾節(jié)點add方法源碼:(jdk1.8與之不同,此處為jdk16)
public boolean add(E e) {
linkLast(e);
return true;
}
--------------------------------------
void linkLast(E e) {
final Node<E> l = last; //初始為null
final Node<E> newNode = new Node<>(l, e, null);
//參數(shù)1:位上一個節(jié)點的內(nèi)存地址,參數(shù)2:e為插入的數(shù)據(jù),參數(shù)3:下一個節(jié)點的內(nèi)存地址
last = newNode; // 最后節(jié)點為新節(jié)點
if (l == null) //如果newNode的前一個節(jié)點為null,則將新節(jié)點賦給first
first = newNode;
else
l.next = newNode; //尾節(jié)點下一個節(jié)點為新節(jié)點
size++;//大小
modCount++;//操作數(shù)
}4.5-LinkedList和ArrayList
LinkedList和ArrayList方法一樣,只是底層實現(xiàn)不一樣。ArrayList底層為數(shù)組存儲,LinkedList是以雙向鏈表存儲。LinkedList集合沒有初始化容量。最初這個鏈表中沒有任何元素。first和last引用都是null。
鏈表的優(yōu)點:
由于鏈表上的元素在空間存儲上內(nèi)存地址不連續(xù)。
所以隨機增刪元素的時候不會有大量元素位移,因此隨機增刪效率較高。
在以后的開發(fā)中,如果遇到隨機增刪集合中元素的業(yè)務(wù)比較多時,建議
使用LinkedList。
鏈表的缺點:
不能通過數(shù)學(xué)表達(dá)式計算被查找元素的內(nèi)存地址,每一次查找都是從頭
節(jié)點開始遍歷,直到找到為止。所以LinkedList集合檢索/查找的效率
較低。
ArrayList:把檢索發(fā)揮到極致。(末尾添加元素效率還是很高的。)
LinkedList:把隨機增刪發(fā)揮到極致。
加元素都是往末尾添加,所以ArrayList用的比LinkedList多。
4.6 -Vector
1、底層也是一個數(shù)組。
2、初始化容量:10
3、怎么擴容的?
擴容之后是原容量的2倍。
10--> 20 --> 40 --> 80
4、Vector中所有的方法都是線程同步的,都帶有synchronized關(guān)鍵字,
是線程安全的。效率比較低,使用較少了。
5、怎么將一個線程不安全的ArrayList集合轉(zhuǎn)換成線程安全的呢?
使用集合工具類:
java.util.Collections;
java.util.Collection 是集合接口。
java.util.Collections 是集合工具類。
Collections.synchronizedList();//將及格轉(zhuǎn)換為線程安全的。
5-Set
5.1 -概述
- Set集合也是一個接口,繼承自Collection,與List類似,都需要通過實現(xiàn)類來進(jìn)行操作。
- 特點
- 不允許包含重復(fù)的值
- 沒有索引(就不能使用普通的for循環(huán)進(jìn)行遍歷)
import java.util.HashSet;
import java.util.Set;
/**
* @author Mr.樂
* @Description Set集合
*/
public class Demo01 {
public static void main(String[] args) {
//使用多態(tài),父類的引用指向子類對象
Set<String> set = new HashSet<>();
//添加元素
set.add("黃固");
set.add("歐陽鋒");
set.add("段智興");
set.add("洪七公");
set.add("段智興");
System.out.println(set);//打印集合
//[洪七公, 黃固, 歐陽鋒, 段智興]
//HashSet集合對于元素的讀寫順序不做保證
//相同的元素,多次存儲,只能保留一個,并且不會報錯
//List集合可以存儲重復(fù)元素,Set集合不行
}
}例:雙色球
import java.util.Random;
import java.util.TreeSet;
/**
* @author Mr.樂
* @Description 雙色球 -Set版
*/
public class Demo02 {
public static void main(String[] args) {
Random ran = new Random();//創(chuàng)建隨機類對象
int blueBall = ran.nextInt(16) + 1;
// HashSet<Integer> redBalls = new HashSet<>();//創(chuàng)建集合用來存儲紅球
TreeSet<Object> redBalls = new TreeSet<>();//TreeSet集合自帶排序規(guī)則
while (redBalls.size() < 6){
redBalls.add(ran.nextInt(33) + 1);//將當(dāng)前生成的紅球直接存進(jìn)集合中
//因為Set集合不能存儲重復(fù)的元素,所以去重的操作可以省略不做。
}
System.out.println("紅球:" + redBalls + "籃球 [" + blueBall + "]");
}
}5.2 -哈希值
Set集合的去重原理使用的是哈希值。
哈希值就是JDK根據(jù)對象地址 或者 字符串 或者數(shù)值 通過自己內(nèi)部的計算出來的一個整數(shù)類型數(shù)據(jù)
public int hashCode()- 用來獲取哈希值,來自于Object頂層類- 對象的哈希值特點
- 同一個對象多次調(diào)用
hashCode()方法,得到的結(jié)果是相同的。- 默認(rèn)情況下,不同的對象的哈希值也是不同的(特殊情況除外)
/**
* @author Mr.樂
* @Description 哈希值
*/
public class Demo03 {
public static void main(String[] args) {
//相同對象哈希值相同
System.out.println("張三".hashCode());//774889
System.out.println("張三".hashCode());//774889
//不同對象哈希值不同
System.out.println(new Object().hashCode());
System.out.println(new Object().hashCode());
//不同的對象的哈希值也有可能相同,例外情況
System.out.println("輅鵝".hashCode());//1179395
System.out.println("較鴉".hashCode());//1179395
System.out.println("輒鸇".hashCode());//1179395
System.out.println("輔鷨".hashCode());//1179395
}
}5.3 -HashSet去重原理
- HashSet集合的特點
- 底層結(jié)構(gòu)是“哈希表”
- 集合對于讀寫順序不做保證
- 沒有索引
- Set集合中的內(nèi)容不能重復(fù)
/**
* @author Mr.樂
* @Description HashSet去重原理
*/
public class Demo04 {
public static void main(String[] args) {
HashSet<Student> set = new HashSet<>();
//添加元素
set.add(new Student("黃固",28));
set.add(new Student("歐陽鋒",38));
set.add(new Student("段智興",48));
set.add(new Student("洪七公",40));
set.add(new Student("段智興",48));
//從程序的角度來考慮,兩個段智興不是同一個對象,都有自己的存儲空間,所以哈希值也不一樣。
for (Student stu : set) {
System.out.println(stu);
}
/*
重寫hashcode和equals
Student{name='段智興', age=48}
Student{name='歐陽鋒', age=38}
Student{name='洪七公', age=40}
Student{name='黃固', age=28}
*/
}
}5.4 -LinkedHashSet
- 特點
- LinkedHashSet是哈希表和鏈表實現(xiàn)的Set接口,具有可預(yù)測的讀寫順序。
- 有鏈表來保證元素有序
- 有哈希表來保證元素的唯一性
/**
* @author Mr.樂
* @Description LinkedHashSet
*/
public class Demo05 {
public static void main(String[] args) {
LinkedHashSet<String> set = new LinkedHashSet<>();
//添加元素
set.add("黃固");
set.add("歐陽鋒");
set.add("段智興");
set.add("洪七公");
set.add("段智興");//重復(fù)的元素不能存進(jìn)去
System.out.println(set);//打印集合 [黃固, 歐陽鋒, 段智興, 洪七公]
}
}5.5 -TreeSet
1、TreeSet集合底層實際上是一個TreeMap
2、TreeMap集合底層是一個二叉樹。
3、放到TreeSet集合中的元素,等同于放到TreeMap集合key部分了。
4、TreeSet集合中的元素:無序不可重復(fù),但是可以按照元素的大小順序自動排序。
import java.util.TreeSet;
public class TreeSetTest02 {
public static void main(String[] args) {
// 創(chuàng)建一個TreeSet集合
TreeSet<String> ts = new TreeSet<>();
// 添加String
ts.add("zhangsan");
ts.add("lisi");
ts.add("wangwu");
ts.add("zhangsi");
ts.add("wangliu");
// 遍歷
for(String s : ts){
// 按照字典順序,升序!
System.out.println(s);
}
/*
lisi
wangliu
wangwu
zhangsan
zhangsi
*/
TreeSet<Integer> ts2 = new TreeSet<>();
ts2.add(100);
ts2.add(200);
ts2.add(900);
ts2.add(800);
ts2.add(600);
ts2.add(10);
for(Integer elt : ts2){
// 升序!
System.out.println(elt);
}
}
}5.5.1 -自定義排序規(guī)則
對于自定義的類無法排序,因為類中對象之間沒有比較規(guī)則,不知道誰大誰小。
/**
* @author Mr.樂
* @Description 自定義比較器
*/
import java.util.TreeSet;
public class TreeSetTest04 {
public static void main(String[] args) {
Customer c1 = new Customer(32);
Customer c2 = new Customer(20);
Customer c3 = new Customer(30);
Customer c4 = new Customer(25);
// 創(chuàng)建TreeSet集合
TreeSet<Customer> customers = new TreeSet<>();
// 添加元素
customers.add(c1);
customers.add(c2);
customers.add(c3);
customers.add(c4);
// 遍歷
for (Customer c : customers){
System.out.println(c);
}
}
}
// 放在TreeSet集合中的元素需要實現(xiàn)java.lang.Comparable接口。
// 并且實現(xiàn)compareTo方法。equals可以不寫。
class Customer implements Comparable<Customer>{
int age;
public Customer(int age){
this.age = age;
}
// 需要在這個方法中編寫比較的邏輯,或者說比較的規(guī)則,按照什么進(jìn)行比較!
// k.compareTo(t.key)
// 拿著參數(shù)k和集合中的每一個k進(jìn)行比較,返回值可能是>0 <0 =0
// 比較規(guī)則最終還是由程序員指定的:例如按照年齡升序。或者按照年齡降序。
@Override
public int compareTo(Customer c) { // c1.compareTo(c2);
return c.age - this.age;
}
public String toString(){
return "Customer[age="+age+"]";
}
}匿名內(nèi)部類方式
public class TreeSetTest05 {
public static void main(String[] args) {
// TreeSet<Student> ts = new TreeSet<>();//默認(rèn)排序規(guī)則
TreeSet<Student> ts = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
int res = o1.getAge() - o2.getAge();
return 0 == res ? o1.getName().compareTo(o2.getName()) : res;
//三目運算符 等于零用姓名排序
}
});//默認(rèn)排序規(guī)則
//添加元素
ts.add(new Student("Andy",19));
ts.add(new Student("Jack",18));
ts.add(new Student("Tom",21));
ts.add(new Student("Lucy",17));
ts.add(new Student("Bob",21)); //當(dāng)年齡相同時,按照姓名的字典順序排序
for (Student stu : ts) {
System.out.println(stu);
}
}
}Comparable和Comparator怎么選擇呢?
當(dāng)比較規(guī)則不會發(fā)生改變的時候,或者說當(dāng)比較規(guī)則只有1個的時候,建議實現(xiàn)Comparable接口。
如果比較規(guī)則有多個,并且需要多個比較規(guī)則之間頻繁切換,建議使用Comparator接口。
6 -Map
6.1 -概述
- 雙列集合:用來存儲鍵值對的集合。
interface Map<K,V>: K(key)鍵 ,V(value)值- 將鍵映射到值的對象,不能出現(xiàn)重復(fù)的鍵,每個鍵最多可以映射到一個值
1、Map和Collection沒有繼承關(guān)系。
2、Map集合以key和value的方式存儲數(shù)據(jù):鍵值對
key和value都是引用數(shù)據(jù)類型。
key和value都是存儲對象的內(nèi)存地址。
key起到主導(dǎo)的地位,value是key的一個附屬品。
例子:
| 學(xué)號(Key) | 姓名(Value) |
|---|---|
| STU001 | 張三 |
| STU002 | 李四 |
| STU003 | 張三 |
6.2 -Map的基本方法
| 學(xué)號(Key) | 姓名(Value) |
|---|---|
| STU001 | 張三 |
| STU002 | 李四 |
| STU003 | 張三 |
import java.util.HashMap;
import java.util.Map;
/**
* @author Mr.樂
* @Description 集合的基本方法
*/
public class Map01 {
public static void main(String[] args) {
Map<String,String> map = new HashMap<>();
map.put("STU001","Andy");
map.put("STU002","Jack");
map.put("STU003","Tom");
map.put("STU004","Bob");
map.put("STU004","Smith");//設(shè)置(修改)
//如果鍵不存在,則表示添加元素。如果鍵存在,則表示設(shè)置值。
//刪除
System.out.println(map.remove("STU003")); //Tom
//判斷是否包含
System.out.println(map.containsKey("STU003")); //false
System.out.println(map.containsKey("STU004")); //true
System.out.println("-----------------------");
System.out.println(map.containsValue("Tom")); //false
System.out.println(map.containsValue("Smith")); //true
System.out.println("-----------------------");
System.out.println(map.isEmpty());//判斷集合是否為空 false
map.clear();//清空集合
System.out.println(map.isEmpty()); //true
System.out.println(map); //{}
}
}6.3 -Map集合的獲取功能
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* @author Mr.樂
* @Description
*/
public class map_get {
public static void main(String[] args) {
Map<String,String> map = new HashMap<>();
map.put("STU001","Andy");
map.put("STU002","Jack");
map.put("STU003","Tom");
map.put("STU004","Bob");
//get通過鍵獲取值
System.out.println(map.get("STU003"));
System.out.println("------------------");
//keySet 獲取所有鍵的Set集合
Set<String> keySet = map.keySet();
System.out.println(keySet);
//values 獲取所有值的Collection集合
Collection<String> values = map.values();
System.out.println(values);
//entrySet 獲取所有鍵值對對象的Set集合
Set<Map.Entry<String, String>> es = map.entrySet();
//Map集合通過entrySet()方法轉(zhuǎn)換成的這個Set集合,Set集合中元素的類型是 Map.Entry<K,V>
//Map.Entry和String一樣,都是一種類型的名字,只不過:Map.Entry是靜態(tài)內(nèi)部類,是Map中的靜態(tài)內(nèi)部類
System.out.println(es);
//[STU001=Andy, STU003=Tom, STU002=Jack, STU004=Bob]
for (Map.Entry<String, String> entry:es){
System.out.println("key:"+entry.getKey()+" "+"value:"+entry.getValue());
}
/*
key:STU001 value:Andy
key:STU003 value:Tom
key:STU002 value:Jack
key:STU004 value:Bob
*/
}
}6.4 -哈希表
通過 數(shù)組 + 鏈表 實現(xiàn)的一種數(shù)據(jù)結(jié)構(gòu)
哈希表的構(gòu)造方法的參數(shù)是一個長度為16個元素的數(shù)組,通過哈希值 % 16 的值,作為頭節(jié)點在數(shù)組中選擇對應(yīng)的位置,就形成了哈希表。

注:圖轉(zhuǎn)自動力節(jié)點。
6.5 -HashMap
6.5.1 -底層源碼
public class HashMap{
// HashMap底層實際上就是一個數(shù)組。(一維數(shù)組)
Node<K,V>[] table;
// 靜態(tài)的內(nèi)部類HashMap.Node
static class Node<K,V> {
final int hash; // 哈希值(哈希值是key的hashCode()方法的執(zhí)行結(jié)果。hash值通過哈希函數(shù)/算法,可以轉(zhuǎn)換存儲成數(shù)組的下標(biāo)。)
final K key; // 存儲到Map集合中的那個key
V value; // 存儲到Map集合中的那個value
Node<K,V> next; // 下一個節(jié)點的內(nèi)存地址。
}
}6.5.2 -特點
1、無序,不可重復(fù)。
為什么無序? 因為不一定掛到哪個單向鏈表上。
不可重復(fù)是怎么保證的? equals方法來保證HashMap集合的key不可重復(fù)。
如果key重復(fù)了,value會覆蓋。
2、放在HashMap集合key部分的元素其實就是放到HashSet集合中了。
所以HashSet集合中的元素也需要同時重寫hashCode()+equals()方法。
3、HashMap集合的默認(rèn)初始化容量是16,默認(rèn)加載因子是0.75
這個默認(rèn)加載因子是當(dāng)HashMap集合底層數(shù)組的容量達(dá)到75%的時候,數(shù)組以二叉樹開始擴容。
重點,記?。篐ashMap集合初始化容量必須是2的倍數(shù),這也是官方推薦的,
這是因為達(dá)到散列均勻,為了提高HashMap集合的存取效率,所必須的。
6.5.3 -注意
1.向Map集合中存,以及從Map集合中取,都是先調(diào)用key的hashCode方法,然后再調(diào)用equals方法!
equals方法有可能調(diào)用,也有可能不調(diào)用。
拿put(k,v)舉例,什么時候equals不會調(diào)用? k.hashCode()方法返回哈希值, 哈希值經(jīng)過哈希算法轉(zhuǎn)換成數(shù)組下標(biāo)。 數(shù)組下標(biāo)位置上如果是null,equals不需要執(zhí)行。 拿get(k)舉例,什么時候equals不會調(diào)用? k.hashCode()方法返回哈希值, 哈希值經(jīng)過哈希算法轉(zhuǎn)換成數(shù)組下標(biāo)。 數(shù)組下標(biāo)位置上如果是null,equals不需要執(zhí)行。
4.假設(shè)將所有的hashCode()方法返回值固定為某個值,那么會導(dǎo)致底層哈希表變成了 純單向鏈表。
這種情況我們成為:散列分布不均勻。
什么是散列分布均勻?
假設(shè)有100個元素,10個單向鏈表,那么每個單向鏈表上有10個節(jié)點,這是最好的, 是散列分布均勻的。假設(shè)將所有的hashCode()方法返回值都設(shè)定為不一樣的值,可以嗎,有什么問題? 不行,因為這樣的話導(dǎo)致底層哈希表就成為一維數(shù)組了,沒有鏈表的概念了。 也是散列分布不均勻。散列分布均勻需要你重寫hashCode()方法時有一定的技巧。
7 -Properties
Properties是一個Map集合,繼承Hashtable,Properties的key和value都是String類型。 Properties被稱為屬性類對象。 Properties是線程安全的。
7.1 -方法
import java.io.IOException;
import java.util.Properties;
import java.util.Set;
/**
* @author Mr.樂
* @Description Properties特有方法
*/
public class Properties01 {
public static void main(String[] args) throws IOException {
Properties prop = new Properties();
final String SRC = "./myConf.ini";//定義配置信息存儲路徑
// mySave(prop,SRC);//存儲配置文件
myLoad(prop,SRC);//加載配置文件信息
//PASSWORD<--->123456
//DATABASE<--->YX2115
//PORT<--->3306
//USERNAME<--->root
}
private static void myLoad(Properties prop, String src) throws IOException {
FileReader fr = new FileReader(src);
prop.load(fr);//通過流,加載指定路徑的配置文件
fr.close();
//遍歷
Set<String> keySet = prop.stringPropertyNames();//獲取對象鍵的Set集合
for (String key : keySet) {
System.out.println(key + "<--->" + prop.getProperty(key));//通過鍵拿到值
}
}
private static void mySave(Properties prop, String src) throws IOException {
//將配置信息存儲到對象中
prop.setProperty("USERNAME","root");
prop.setProperty("PASSWORD","123456");
prop.setProperty("DATABASE","YX2115");
prop.setProperty("PORT","3306");
//寫入文件
FileWriter fw = new FileWriter(src);//創(chuàng)建輸出流對象
prop.store(fw,"MyDataBase Configure!~");
fw.close();
}
}8 -總結(jié)
本篇文章介紹了集合的常用方法以及個別集合的底層是如何實現(xiàn)的。介紹了集合的繼承與實現(xiàn)結(jié)構(gòu)。各個集合的擴容方式及擴容大小以及各個集合的優(yōu)點和用途。希望大家可以根據(jù)本篇文章可以更加深刻的理解java中的集合。
到此這篇關(guān)于java集合超詳細(xì)的文章就介紹到這了,更多相關(guān)java集合內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring實現(xiàn)HikariCP連接池的示例代碼
在SpringBoot 2.0中,我們使用默認(rèn)連接池是HikariCP,本文講一下HikariCP的具體使用,具有一定的參考價值,感興趣的可以了解一下2021-08-08
Redis Lettuce連接redis集群實現(xiàn)過程詳細(xì)講解
這篇文章主要介紹了Redis Lettuce連接redis集群實現(xiàn)過程,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧2023-01-01
Springboot整合nacos報錯無法連接nacos的解決
這篇文章主要介紹了Springboot整合nacos報錯無法連接nacos的解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-06-06
使用ScheduledThreadPoolExecutor踩過最痛的坑
這篇文章主要介紹了使用ScheduledThreadPoolExecutor踩過最痛的坑及解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-08-08
解決Mybatis-plus和pagehelper依賴沖突的方法示例
這篇文章主要介紹了解決Mybatis-plus和pagehelper依賴沖突的方法示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04
Java使用自動化部署工具Gradle中的任務(wù)設(shè)定教程
Grandle使用同樣運行于JVM上的Groovy語言編寫,本文會對此進(jìn)行初步夠用的講解,接下來我們就一起來看一下Java使用自動化部署工具Gradle中的任務(wù)設(shè)定教程:2016-06-06

