java集合超詳細(xì)(最新推薦)
1 - 概述
所有的集合類和集合接口都在java.util包下。
在內(nèi)存中申請(qǐng)一塊空間用來存儲(chǔ)數(shù)據(jù),在Java中集合就是替換掉定長(zhǎng)的數(shù)組的一種引用數(shù)據(jù)類型。
2 - 集合與數(shù)組的區(qū)別
長(zhǎng)度區(qū)別
數(shù)組長(zhǎng)度固定,定義長(zhǎng)了造成內(nèi)存空間的浪費(fèi),定義短了不夠用。
集合大小可以變,用多少空間拿多少空間。
內(nèi)容區(qū)別
數(shù)組可以存儲(chǔ)基本數(shù)據(jù)類型和引用數(shù)據(jù)類型
集合中能存儲(chǔ)引用數(shù)據(jù)類型(存儲(chǔ)的為對(duì)象的內(nèi)存地址)
list.add(100);//為自動(dòng)裝箱,100為Integer包裝的
元素區(qū)別
數(shù)組中只能存儲(chǔ)同一種類型成員
集合中可以存儲(chǔ)不同類型數(shù)據(jù)(一般情況下也只存儲(chǔ)同一種類型的數(shù)據(jù))
集合結(jié)構(gòu)
在java中每一個(gè)不同的集合,底層會(huì)對(duì)應(yīng)不同的數(shù)據(jù)結(jié)構(gòu)。往不同的集合中
存儲(chǔ)元素,等于將數(shù)據(jù)放到了不同的數(shù)據(jù)結(jié)構(gòu)當(dāng)中。什么是數(shù)據(jù)結(jié)構(gòu)?數(shù)據(jù)存儲(chǔ)的
結(jié)構(gòu)就是數(shù)據(jù)結(jié)構(gòu)。不同的數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)存儲(chǔ)方式不同。
- 單列集合 Collection
- List可以重復(fù):ArrayList/LinkedList
- Set不可重復(fù):HashSet/TreeSet
(大量文字插入會(huì)導(dǎo)致圖片不清,所以在此進(jìn)行更詳細(xì)的描述)
- List特點(diǎn):此處順序并不是大小順序,而是存入數(shù)據(jù)的先后順序。有序因?yàn)長(zhǎng)ist集合都有下標(biāo),下標(biāo)從0開始,以遞增。
- Set特點(diǎn):取出順序不一定為存入順序,另外Set集合沒有下標(biāo)。
- ArrayList是非線程安全的。
- HashSet集合在new的時(shí)候,底層實(shí)際上new了一個(gè)HashMap集合。向HashSet集合中存儲(chǔ)元素,實(shí)際上是存儲(chǔ)到了HashMap的key中了。HashMap集合是一個(gè)Hash表數(shù)據(jù)結(jié)構(gòu)。
- SortedSet集合存儲(chǔ)元素的特點(diǎn):由于繼承了Set集合,所以他的特點(diǎn)也是無序不可重復(fù),但是放在SortedSet集合中的元素可以自動(dòng)排序。放到該集合中的元素是自動(dòng)按照大小順序排序的。
- TreeSet集合底層實(shí)際上是TreeMap。TreeSet集合在new的時(shí)候,底層實(shí)際上new了一個(gè)TreeMap集合。向TreeSet集合中存儲(chǔ)元素,實(shí)際上是存儲(chǔ)到了TreeMap的key中了。TreeMap集合是一個(gè)二叉樹數(shù)據(jù)結(jié)構(gòu)。
雙列集合Map:HashMap/TreeMap
粗體是接口 斜體是實(shí)現(xiàn)類
3- Collection集合
3.1 - 概述
單列集合的頂層接口,既然是接口就不能直接使用,需要通過實(shí)現(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() | 返回集合中元素個(gè)數(shù) |
import java.util.ArrayList; import java.util.Collection; /** * @author Mr.樂 * @Description */ public class Collection_01 { public static void main(String[] args) { //父類的引用指向子類的對(duì)象,形成多態(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 //獲取集合中元素個(gè)數(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集合的遍歷方式 //因?yàn)闆]有索引的概念,所以Collection集合不能使用fori進(jìn)行遍歷 //增強(qiáng)版for循環(huán),其實(shí)底層使用的也是迭代器,在字節(jié)碼文件中查看 for (String str : con) { System.out.print(str + "\t"); } System.out.println();//換行 //迭代器,集合專屬的遍歷工具 Iterator<String> it = con.iterator();//創(chuàng)建迭代器對(duì)象 while (it.hasNext()){//判斷下一個(gè)位置是否有元素 System.out.print(it.next() + "\t");//獲取到下一個(gè)位置的元素 } } }
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(); // 注意:此時(shí)獲取的迭代器,指向的是那是集合中沒有元素狀態(tài)下的迭代器。 // 一定要注意:集合結(jié)構(gòu)只要發(fā)生改變,迭代器必須重新獲取。 // 當(dāng)集合結(jié)構(gòu)發(fā)生了改變,迭代器沒有重新獲取時(shí),調(diào)用next()方法時(shí):java.util.ConcurrentModificationException Iterator it = c.iterator(); // 添加元素 c.add(1); // Integer類型 c.add(2); c.add(3); // 獲取迭代器 //Iterator it = c.iterator(); /*while(it.hasNext()){ // 編寫代碼時(shí)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)下一次的時(shí)候并沒有重新獲取迭代器,所以會(huì)出現(xiàn)異常:java.util.ConcurrentModificationException // 出異常根本原因是:集合中元素刪除了,但是沒有更新迭代器(迭代器不知道集合變化了) //c2.remove(o); // 直接通過集合去刪除元素,沒有通知迭代器。(導(dǎo)致迭代器的快照和原集合狀態(tài)不同。) // 使用迭代器來刪除可以嗎? // 迭代器去刪除時(shí),會(huì)自動(dòng)更新迭代器,并且更新集合(刪除集合中的元素)。 it2.remove(); // 刪除的一定是迭代器指向的當(dāng)前元素。 System.out.println(o); } System.out.println(c2.size()); //0 } }
4-List
原型ArrayList<E>
- ArrayList是一個(gè)List接口的實(shí)現(xiàn)類,底層使用的是一個(gè)可以調(diào)整大小的數(shù)組實(shí)現(xiàn)的。
<E>
:是一種特殊的數(shù)據(jù)類型(引用數(shù)據(jù)類型) -- 泛型- ArrayList<String> 或者 ArrayList<Integer> 或者 ArrayList<Student>
4.1 - ArrayList構(gòu)造和添加方法
方法名 | 說明 |
---|---|
public ArrayList<E>() | 創(chuàng)建一個(gè)空集合 |
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,"譚詠麟");//插入元素方法索引值不能大于集合中元素個(gè)數(shù) // list.add(4,"譚詠麟");//表示在集合中最后位置插入元素,與追加相同 list.add(1,"譚詠麟");//指定位置插入元素,索引位置之后的元素會(huì)自動(dòng)向后進(jìn)行移動(dòng) 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) | 獲取指定索引對(duì)應(yīng)的元素 |
public int size() | 獲取結(jié)合中元素個(gè)數(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));//通過指定索引位置獲取集合元素 //獲取集合元素個(gè)數(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(); //增強(qiáng)版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()){//判斷下一個(gè)位置是否有元素 System.out.print(it.next() + "\t"); //next方法表示獲取下一個(gè)位置的元素 } System.out.println(); //Stream流 list.stream().forEach(System.out::println); } }
4.3 -ArrayList實(shí)現(xiàn)原理
底層代碼:
屬性:
DEFAULT_CAPACITY = 10 默認(rèn)長(zhǎng)度,初始化容量為10Object[] EMPTY_ELEMENTDATA = {} //有參構(gòu)造所創(chuàng)建Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {} //無參構(gòu)造所創(chuàng)建的Object[] elementData;底層為Object類型的數(shù)組,存儲(chǔ)的元素都在此。int size 實(shí)際存放的個(gè)數(shù)
構(gòu)造方法 :
//一個(gè)參數(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ù)組的長(zhǎng)度; //參數(shù)如果等于零,EMPTY_ELEMENTDATA; //參數(shù)如果小于0,拋出異常。 //無參構(gòu)造 public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } //DEFAULTCAPACITY_EMPTY_ELEMENTDATA new對(duì)象時(shí)默認(rèn)為0 當(dāng)添加第一個(gè)元素的時(shí)候,數(shù)組擴(kuò)容至10
add方法源碼:(jdk1.8與之不同,此處為jdk16)
//源碼 public boolean add(E e) { modCount++;//操作次數(shù) add(e, elementData, size); //e 操作對(duì)象; 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; //最小需要長(zhǎng)度 } ---------------------------------------------------------- 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ù)組長(zhǎng)度>0或者數(shù)組不是無參構(gòu)造構(gòu)建的 int newCapacity = ArraysSupport.newLength(oldCapacity, //舊數(shù)組的長(zhǎng)度 minCapacity - oldCapacity, /* minimum growth */ //最小需要長(zhǎng)度-舊數(shù)組的長(zhǎng)度 大于0代表空間不足 oldCapacity >> 1 /* preferred growth */); //二進(jìn)制位右移1位 位舊數(shù)組長(zhǎng)度/2 return elementData = Arrays.copyOf(elementData, newCapacity); 將數(shù)據(jù)放入新數(shù)組中 } else { return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)]; //數(shù)組長(zhǎng)度 DEFAULT_CAPACITY為10 此處代表無參構(gòu)造默認(rèn)長(zhǎng)度為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 擴(kuò)容1.5倍 minGrowth>prefGrowth為需要多少給多少 if (newLength - MAX_ARRAY_LENGTH <= 0) { //MAX_ARRAY_LENGTH為int最大值 表示新數(shù)組長(zhǎng)度如果小于int的最大值 return newLength; } return hugeLength(oldLength, minGrowth); //返回int最大值 }
ArrayList集合底層是數(shù)組,怎么優(yōu)化?
盡可能少的擴(kuò)容。因?yàn)閿?shù)組擴(kuò)容效率比較低,建議在使用ArrayList集合 的時(shí)候預(yù)估計(jì)元素的個(gè)數(shù),給定一個(gè)初始化容量。數(shù)組優(yōu)點(diǎn):
檢索效率比較高。(每個(gè)元素占用空間大小相同,內(nèi)存地址是連續(xù)的,知道首元素內(nèi)存地址,
然后知道下標(biāo),通過數(shù)學(xué)表達(dá)式計(jì)算出元素的內(nèi)存地址,所以檢索效率最高。)數(shù)組缺點(diǎn):
隨機(jī)增刪元素效率比較低。
另外數(shù)組無法存儲(chǔ)大數(shù)據(jù)量。(很難找到一塊非常巨大的連續(xù)的內(nèi)存空間。)向數(shù)組末尾添加元素,效率很高,不受影響。
4.4 -LinkedList實(shí)現(xiàn)原理
底層代碼
屬性:
transient int size = 0;//初始長(zhǎng)度 transient Node<E> first;//頭節(jié)點(diǎn) transient Node<E> last;//尾節(jié)點(diǎn)
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:位上一個(gè)節(jié)點(diǎn)的內(nèi)存地址,參數(shù)2:e為插入的數(shù)據(jù),參數(shù)3:下一個(gè)節(jié)點(diǎn)的內(nèi)存地址 last = newNode; // 最后節(jié)點(diǎn)為新節(jié)點(diǎn) if (l == null) //如果newNode的前一個(gè)節(jié)點(diǎn)為null,則將新節(jié)點(diǎn)賦給first first = newNode; else l.next = newNode; //尾節(jié)點(diǎn)下一個(gè)節(jié)點(diǎn)為新節(jié)點(diǎn) size++;//大小 modCount++;//操作數(shù) }
4.5-LinkedList和ArrayList
LinkedList和ArrayList方法一樣,只是底層實(shí)現(xiàn)不一樣。ArrayList底層為數(shù)組存儲(chǔ),LinkedList是以雙向鏈表存儲(chǔ)。LinkedList集合沒有初始化容量。最初這個(gè)鏈表中沒有任何元素。first和last引用都是null。
鏈表的優(yōu)點(diǎn):
由于鏈表上的元素在空間存儲(chǔ)上內(nèi)存地址不連續(xù)。
所以隨機(jī)增刪元素的時(shí)候不會(huì)有大量元素位移,因此隨機(jī)增刪效率較高。
在以后的開發(fā)中,如果遇到隨機(jī)增刪集合中元素的業(yè)務(wù)比較多時(shí),建議
使用LinkedList。
鏈表的缺點(diǎn):
不能通過數(shù)學(xué)表達(dá)式計(jì)算被查找元素的內(nèi)存地址,每一次查找都是從頭
節(jié)點(diǎn)開始遍歷,直到找到為止。所以LinkedList集合檢索/查找的效率
較低。
ArrayList:把檢索發(fā)揮到極致。(末尾添加元素效率還是很高的。)
LinkedList:把隨機(jī)增刪發(fā)揮到極致。
加元素都是往末尾添加,所以ArrayList用的比LinkedList多。
4.6 -Vector
1、底層也是一個(gè)數(shù)組。
2、初始化容量:10
3、怎么擴(kuò)容的?
擴(kuò)容之后是原容量的2倍。
10--> 20 --> 40 --> 80
4、Vector中所有的方法都是線程同步的,都帶有synchronized關(guān)鍵字,
是線程安全的。效率比較低,使用較少了。
5、怎么將一個(gè)線程不安全的ArrayList集合轉(zhuǎn)換成線程安全的呢?
使用集合工具類:
java.util.Collections;
java.util.Collection 是集合接口。
java.util.Collections 是集合工具類。
Collections.synchronizedList();//將及格轉(zhuǎn)換為線程安全的。
5-Set
5.1 -概述
- Set集合也是一個(gè)接口,繼承自Collection,與List類似,都需要通過實(shí)現(xiàn)類來進(jìn)行操作。
- 特點(diǎ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),父類的引用指向子類對(duì)象 Set<String> set = new HashSet<>(); //添加元素 set.add("黃固"); set.add("歐陽鋒"); set.add("段智興"); set.add("洪七公"); set.add("段智興"); System.out.println(set);//打印集合 //[洪七公, 黃固, 歐陽鋒, 段智興] //HashSet集合對(duì)于元素的讀寫順序不做保證 //相同的元素,多次存儲(chǔ),只能保留一個(gè),并且不會(huì)報(bào)錯(cuò) //List集合可以存儲(chǔ)重復(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)建隨機(jī)類對(duì)象 int blueBall = ran.nextInt(16) + 1; // HashSet<Integer> redBalls = new HashSet<>();//創(chuàng)建集合用來存儲(chǔ)紅球 TreeSet<Object> redBalls = new TreeSet<>();//TreeSet集合自帶排序規(guī)則 while (redBalls.size() < 6){ redBalls.add(ran.nextInt(33) + 1);//將當(dāng)前生成的紅球直接存進(jìn)集合中 //因?yàn)镾et集合不能存儲(chǔ)重復(fù)的元素,所以去重的操作可以省略不做。 } System.out.println("紅球:" + redBalls + "籃球 [" + blueBall + "]"); } }
5.2 -哈希值
Set集合的去重原理使用的是哈希值。
哈希值就是JDK根據(jù)對(duì)象地址 或者 字符串 或者數(shù)值 通過自己內(nèi)部的計(jì)算出來的一個(gè)整數(shù)類型數(shù)據(jù)
public int hashCode()
- 用來獲取哈希值,來自于Object頂層類- 對(duì)象的哈希值特點(diǎn)
- 同一個(gè)對(duì)象多次調(diào)用
hashCode()
方法,得到的結(jié)果是相同的。- 默認(rèn)情況下,不同的對(duì)象的哈希值也是不同的(特殊情況除外)
/** * @author Mr.樂 * @Description 哈希值 */ public class Demo03 { public static void main(String[] args) { //相同對(duì)象哈希值相同 System.out.println("張三".hashCode());//774889 System.out.println("張三".hashCode());//774889 //不同對(duì)象哈希值不同 System.out.println(new Object().hashCode()); System.out.println(new Object().hashCode()); //不同的對(duì)象的哈希值也有可能相同,例外情況 System.out.println("輅鵝".hashCode());//1179395 System.out.println("較鴉".hashCode());//1179395 System.out.println("輒鸇".hashCode());//1179395 System.out.println("輔鷨".hashCode());//1179395 } }
5.3 -HashSet去重原理
- HashSet集合的特點(diǎn)
- 底層結(jié)構(gòu)是“哈希表”
- 集合對(duì)于讀寫順序不做保證
- 沒有索引
- 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)); //從程序的角度來考慮,兩個(gè)段智興不是同一個(gè)對(duì)象,都有自己的存儲(chǔ)空間,所以哈希值也不一樣。 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
- 特點(diǎn)
- LinkedHashSet是哈希表和鏈表實(shí)現(xiàn)的Set接口,具有可預(yù)測(cè)的讀寫順序。
- 有鏈表來保證元素有序
- 有哈希表來保證元素的唯一性
/** * @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集合底層實(shí)際上是一個(gè)TreeMap
2、TreeMap集合底層是一個(gè)二叉樹。
3、放到TreeSet集合中的元素,等同于放到TreeMap集合key部分了。
4、TreeSet集合中的元素:無序不可重復(fù),但是可以按照元素的大小順序自動(dòng)排序。
import java.util.TreeSet; public class TreeSetTest02 { public static void main(String[] args) { // 創(chuàng)建一個(gè)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ī)則
對(duì)于自定義的類無法排序,因?yàn)轭愔袑?duì)象之間沒有比較規(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集合中的元素需要實(shí)現(xiàn)java.lang.Comparable接口。 // 并且實(shí)現(xiàn)compareTo方法。equals可以不寫。 class Customer implements Comparable<Customer>{ int age; public Customer(int age){ this.age = age; } // 需要在這個(gè)方法中編寫比較的邏輯,或者說比較的規(guī)則,按照什么進(jìn)行比較! // k.compareTo(t.key) // 拿著參數(shù)k和集合中的每一個(gè)k進(jìn)行比較,返回值可能是>0 <0 =0 // 比較規(guī)則最終還是由程序員指定的:例如按照年齡升序?;蛘甙凑漳挲g降序。 @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; //三目運(yùn)算符 等于零用姓名排序 } });//默認(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)年齡相同時(shí),按照姓名的字典順序排序 for (Student stu : ts) { System.out.println(stu); } } }
Comparable和Comparator怎么選擇呢?
當(dāng)比較規(guī)則不會(huì)發(fā)生改變的時(shí)候,或者說當(dāng)比較規(guī)則只有1個(gè)的時(shí)候,建議實(shí)現(xiàn)Comparable接口。
如果比較規(guī)則有多個(gè),并且需要多個(gè)比較規(guī)則之間頻繁切換,建議使用Comparator接口。
6 -Map
6.1 -概述
- 雙列集合:用來存儲(chǔ)鍵值對(duì)的集合。
interface Map<K,V>
: K(key)鍵 ,V(value)值- 將鍵映射到值的對(duì)象,不能出現(xiàn)重復(fù)的鍵,每個(gè)鍵最多可以映射到一個(gè)值
1、Map和Collection沒有繼承關(guān)系。
2、Map集合以key和value的方式存儲(chǔ)數(shù)據(jù):鍵值對(duì)
key和value都是引用數(shù)據(jù)類型。
key和value都是存儲(chǔ)對(duì)象的內(nèi)存地址。
key起到主導(dǎo)的地位,value是key的一個(gè)附屬品。
例子:
學(xué)號(hào)(Key) | 姓名(Value) |
---|---|
STU001 | 張三 |
STU002 | 李四 |
STU003 | 張三 |
6.2 -Map的基本方法
學(xué)號(hào)(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 獲取所有鍵值對(duì)對(duì)象的Set集合 Set<Map.Entry<String, String>> es = map.entrySet(); //Map集合通過entrySet()方法轉(zhuǎn)換成的這個(gè)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ù)組 + 鏈表 實(shí)現(xiàn)的一種數(shù)據(jù)結(jié)構(gòu)
哈希表的構(gòu)造方法的參數(shù)是一個(gè)長(zhǎng)度為16個(gè)元素的數(shù)組,通過哈希值 % 16 的值,作為頭節(jié)點(diǎn)在數(shù)組中選擇對(duì)應(yīng)的位置,就形成了哈希表。
注:圖轉(zhuǎn)自動(dòng)力節(jié)點(diǎn)。
6.5 -HashMap
6.5.1 -底層源碼
public class HashMap{ // HashMap底層實(shí)際上就是一個(gè)數(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)換存儲(chǔ)成數(shù)組的下標(biāo)。) final K key; // 存儲(chǔ)到Map集合中的那個(gè)key V value; // 存儲(chǔ)到Map集合中的那個(gè)value Node<K,V> next; // 下一個(gè)節(jié)點(diǎn)的內(nèi)存地址。 } }
6.5.2 -特點(diǎn)
1、無序,不可重復(fù)。
為什么無序? 因?yàn)椴灰欢⊕斓侥膫€(gè)單向鏈表上。
不可重復(fù)是怎么保證的? equals方法來保證HashMap集合的key不可重復(fù)。
如果key重復(fù)了,value會(huì)覆蓋。
2、放在HashMap集合key部分的元素其實(shí)就是放到HashSet集合中了。
所以HashSet集合中的元素也需要同時(shí)重寫hashCode()+equals()方法。
3、HashMap集合的默認(rèn)初始化容量是16,默認(rèn)加載因子是0.75
這個(gè)默認(rèn)加載因子是當(dāng)HashMap集合底層數(shù)組的容量達(dá)到75%的時(shí)候,數(shù)組以二叉樹開始擴(kuò)容。
重點(diǎn),記?。篐ashMap集合初始化容量必須是2的倍數(shù),這也是官方推薦的,
這是因?yàn)檫_(dá)到散列均勻,為了提高HashMap集合的存取效率,所必須的。
6.5.3 -注意
1.向Map集合中存,以及從Map集合中取,都是先調(diào)用key的hashCode方法,然后再調(diào)用equals方法!
equals方法有可能調(diào)用,也有可能不調(diào)用。
拿put(k,v)舉例,什么時(shí)候equals不會(huì)調(diào)用? k.hashCode()方法返回哈希值, 哈希值經(jīng)過哈希算法轉(zhuǎn)換成數(shù)組下標(biāo)。 數(shù)組下標(biāo)位置上如果是null,equals不需要執(zhí)行。 拿get(k)舉例,什么時(shí)候equals不會(huì)調(diào)用? k.hashCode()方法返回哈希值, 哈希值經(jīng)過哈希算法轉(zhuǎn)換成數(shù)組下標(biāo)。 數(shù)組下標(biāo)位置上如果是null,equals不需要執(zhí)行。
4.假設(shè)將所有的hashCode()方法返回值固定為某個(gè)值,那么會(huì)導(dǎo)致底層哈希表變成了 純單向鏈表。
這種情況我們成為:散列分布不均勻。
什么是散列分布均勻?
假設(shè)有100個(gè)元素,10個(gè)單向鏈表,那么每個(gè)單向鏈表上有10個(gè)節(jié)點(diǎn),這是最好的, 是散列分布均勻的。假設(shè)將所有的hashCode()方法返回值都設(shè)定為不一樣的值,可以嗎,有什么問題? 不行,因?yàn)檫@樣的話導(dǎo)致底層哈希表就成為一維數(shù)組了,沒有鏈表的概念了。 也是散列分布不均勻。散列分布均勻需要你重寫hashCode()方法時(shí)有一定的技巧。
7 -Properties
Properties是一個(gè)Map集合,繼承Hashtable,Properties的key和value都是String類型。 Properties被稱為屬性類對(duì)象。 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";//定義配置信息存儲(chǔ)路徑 // mySave(prop,SRC);//存儲(chǔ)配置文件 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();//獲取對(duì)象鍵的Set集合 for (String key : keySet) { System.out.println(key + "<--->" + prop.getProperty(key));//通過鍵拿到值 } } private static void mySave(Properties prop, String src) throws IOException { //將配置信息存儲(chǔ)到對(duì)象中 prop.setProperty("USERNAME","root"); prop.setProperty("PASSWORD","123456"); prop.setProperty("DATABASE","YX2115"); prop.setProperty("PORT","3306"); //寫入文件 FileWriter fw = new FileWriter(src);//創(chuàng)建輸出流對(duì)象 prop.store(fw,"MyDataBase Configure!~"); fw.close(); } }
8 -總結(jié)
本篇文章介紹了集合的常用方法以及個(gè)別集合的底層是如何實(shí)現(xiàn)的。介紹了集合的繼承與實(shí)現(xiàn)結(jié)構(gòu)。各個(gè)集合的擴(kuò)容方式及擴(kuò)容大小以及各個(gè)集合的優(yōu)點(diǎn)和用途。希望大家可以根據(jù)本篇文章可以更加深刻的理解java中的集合。
到此這篇關(guān)于java集合超詳細(xì)的文章就介紹到這了,更多相關(guān)java集合內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring實(shí)現(xiàn)HikariCP連接池的示例代碼
在SpringBoot 2.0中,我們使用默認(rèn)連接池是HikariCP,本文講一下HikariCP的具體使用,具有一定的參考價(jià)值,感興趣的可以了解一下2021-08-08Redis Lettuce連接redis集群實(shí)現(xiàn)過程詳細(xì)講解
這篇文章主要介紹了Redis Lettuce連接redis集群實(shí)現(xiàn)過程,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧2023-01-01Springboot整合nacos報(bào)錯(cuò)無法連接nacos的解決
這篇文章主要介紹了Springboot整合nacos報(bào)錯(cuò)無法連接nacos的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-06-06使用ScheduledThreadPoolExecutor踩過最痛的坑
這篇文章主要介紹了使用ScheduledThreadPoolExecutor踩過最痛的坑及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-08-08解決Mybatis-plus和pagehelper依賴沖突的方法示例
這篇文章主要介紹了解決Mybatis-plus和pagehelper依賴沖突的方法示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04Java使用自動(dòng)化部署工具Gradle中的任務(wù)設(shè)定教程
Grandle使用同樣運(yùn)行于JVM上的Groovy語言編寫,本文會(huì)對(duì)此進(jìn)行初步夠用的講解,接下來我們就一起來看一下Java使用自動(dòng)化部署工具Gradle中的任務(wù)設(shè)定教程:2016-06-06