java(五)-迭代器,数据结构,List,Set ,TreeSet集合,Collections工具类
day05【迭代器,數(shù)據(jù)結(jié)構(gòu),List,Set ,TreeSet集合,Collections工具類】
主要內(nèi)容
- Collection集合的遍歷方式:
- 迭代器。
- foreach(增強for循環(huán))
- JDK 1.8開始的新技術(shù)Lambda表達(dá)式。
- 數(shù)據(jù)結(jié)構(gòu)
- 是集合的底層,研究數(shù)據(jù)結(jié)構(gòu)是為了選擇使用某種集合。
- List接口
- 元素是有序可重復(fù)有索引的。
- Set接口
- 元素是無序不重復(fù)無索引的。
- Collections是操作集合的工具類。
- 把學(xué)的集合用起來:斗地主的游戲。
教學(xué)目標(biāo)
-
能夠使用迭代器對集合進(jìn)行取元素
- java Iterator<String> it = names.iterator(); while(it.hasNext()){ String rs = it.next(); System.out.println(rs); }
- 能夠說出List集合特點
- 元素是有序,可重復(fù)的,有索引的,底層是基于數(shù)組存儲元素的,查詢快,增刪慢!
-
能夠說出常見的數(shù)據(jù)結(jié)構(gòu)
- 隊列: 先進(jìn)先出
- 棧:先進(jìn)后出,后進(jìn)先出
- 數(shù)組:底層是連續(xù)內(nèi)存區(qū)域,查詢快,增刪慢!
- 鏈表:元素是游離存儲的,查詢慢,首尾操作快!
- 紅黑樹 (HastSet ):增刪改查都很好,可以排序,可以提高檢索數(shù)據(jù)的性能!
-
能夠說出數(shù)組結(jié)構(gòu)特點
- 內(nèi)存中的連續(xù)區(qū)域,每個區(qū)間大小固定,查詢快,增刪慢!
-
能夠說出棧結(jié)構(gòu)特點
- 先進(jìn)后出。
-
能夠說出隊列結(jié)構(gòu)特點
- 先進(jìn)先出
-
能夠說出單向鏈表結(jié)構(gòu)特點
- 元素是游離存儲的,查詢慢, 一端的增刪操作快!
-
能夠說出Set集合的特點
- 元素是無序,不重復(fù),無索引,底層是基于哈希表存儲元素的,曾刪查的性能都很好!!
-
能夠說出哈希表的特點
- JDK 1.8之前是:鏈表+數(shù)組
- JDK 1.8之后是:鏈表+數(shù)組+紅黑樹
-
使用HashSet集合存儲自定義元素
- Set sets = new HashSet<>();
第一章 Iterator迭代器
1.1 Iterator接口
在程序開發(fā)中,經(jīng)常需要遍歷集合中的所有元素。針對這種需求,JDK專門提供了一個接口java.util.Iterator。
想要遍歷Collection集合,那么就要獲取該集合迭代器完成迭代操作,下面介紹一下獲取迭代器的方法:
- public Iterator iterator(): 獲取集合對應(yīng)的迭代器,用來遍歷集合中的元素的。
下面介紹一下迭代的概念:
- 迭代:即Collection集合元素的通用獲取方式。在取元素之前先要判斷集合中有沒有元素,如果有,就把這個元素取出來,繼續(xù)在判斷,如果還有就再取出出來。一直把集合中的所有元素全部取出。這種取出方式專業(yè)術(shù)語稱為迭代。
Iterator接口的常用方法如下:
- public E next():返回迭代的下一個元素。
- public boolean hasNext():如果仍有元素可以迭代,則返回 true。
接下來我們通過案例學(xué)習(xí)如何使用Iterator迭代集合中元素:
public class IteratorDemo {public static void main(String[] args) {// 使用多態(tài)方式 創(chuàng)建對象Collection<String> coll = new ArrayList<String>();// 添加元素到集合coll.add("串串星人");coll.add("吐槽星人");coll.add("汪星人");//遍歷//使用迭代器 遍歷 每個集合對象都有自己的迭代器Iterator<String> it = coll.iterator();// 泛型指的是 迭代出 元素的數(shù)據(jù)類型while(it.hasNext()){ //判斷是否有迭代元素String s = it.next();//獲取迭代出的元素System.out.println(s);}} }tips:
總結(jié)
迭代器的類型和集合的類型一致
可以理解成迭代器是集合的游標(biāo),每次next都會取集合的下一個位置
目標(biāo):Collection集合的遍歷方式。什么是遍歷? 為什么開發(fā)中要遍歷?遍歷就是一個一個的把容器中的元素訪問一遍。開發(fā)中經(jīng)常要統(tǒng)計元素的總和,找最值,找出某個數(shù)據(jù)然后干掉等等業(yè)務(wù)都需要遍歷。Collection集合的遍歷方式是全部集合都可以直接使用的,所以我們學(xué)習(xí)它。 Collection集合的遍歷方式有三種:(1)迭代器。(2)foreach(增強for循環(huán))。(3)JDK 1.8開始之后的新技術(shù)Lambda表達(dá)式(了解)a.迭代器遍歷集合。-- 方法:public Iterator iterator(): 獲取集合對應(yīng)的迭代器,用來遍歷集合中的元素的E next():獲取下一個元素值!boolean hasNext():判斷是否有下一個元素,有返回true ,反之。--流程:1.先獲取當(dāng)前集合的迭代器Iterator<String> it = lists.iterator();2.定義一個while循環(huán),問一次取一次。通過it.hasNext()詢問是否有下一個元素,有就通過it.next()取出下一個元素。小結(jié):記住!快速生成foreach變量的方法:數(shù)組或者集合對象.for 然后回車 就能快速生成
集合不能用for循環(huán)遍歷,因為沒有索引
目標(biāo):Collection集合的遍歷方式。什么是遍歷? 為什么開發(fā)中要遍歷? 遍歷就是一個一個的把容器中的元素訪問一遍。 開發(fā)中經(jīng)常要統(tǒng)計元素的總和,找最值,找出某個數(shù)據(jù)然后干掉等等業(yè)務(wù)都需要遍歷。Collection集合的遍歷方式是全部集合都可以直接使用的,所以我們學(xué)習(xí)它。 Collection集合的遍歷方式有三種:(1)迭代器。(2)foreach(增強for循環(huán))。(3)JDK 1.8開始之后的新技術(shù)Lambda表達(dá)式。b.foreach(增強for循環(huán))遍歷集合。foreach是一種遍歷形式,可以遍歷集合或者數(shù)組。foreach遍歷集合實際上是迭代器遍歷的簡化寫法。foreach遍歷的關(guān)鍵是記住格式:for(被遍歷集合或者數(shù)組中元素的類型 變量名稱 : 被遍歷集合或者數(shù)組){}小結(jié):foreach遍歷集合或者數(shù)組很方便。缺點:foreach遍歷無法知道遍歷到了哪個元素了,因為沒有索引。 Collection集合的遍歷方式有三種:(1)迭代器。(2)foreach(增強for循環(huán))。(3)JDK 1.8開始之后的新技術(shù)Lambda表達(dá)式。 c.JDK 1.8開始之后的新技術(shù)Lambda表達(dá)式。(暫時了解)Collection<String> lists = new ArrayList<>();lists.add("趙敏");lists.add("小昭");lists.add("殷素素");lists.add("周芷若");System.out.println(lists);// [趙敏, 小昭, 殷素素, 周芷若]// slists.forEach(s -> {//箭頭語法 lambda表達(dá)式,可以簡化代碼System.out.println(s);}); // lists.forEach(s -> System.out.println(s));//可以簡化代碼 // lists.forEach(System.out::println);//更加簡化代碼1.2 迭代器的實現(xiàn)原理
我們在之前案例已經(jīng)完成了Iterator遍歷集合的整個過程。當(dāng)遍歷集合時,首先通過調(diào)用t集合的iterator()方法獲得迭代器對象,然后使用hashNext()方法判斷集合中是否存在下一個元素,如果存在,則調(diào)用next()方法將元素取出,否則說明已到達(dá)了集合末尾,停止遍歷元素。
Iterator迭代器對象在遍歷集合時,內(nèi)部采用指針的方式來跟蹤集合中的元素,為了讓初學(xué)者能更好地理解迭代器的工作原理,接下來通過一個圖例來演示Iterator對象迭代元素的過程:
在調(diào)用Iterator的next方法之前,迭代器的索引位于第一個元素之前,不指向任何元素,當(dāng)?shù)谝淮握{(diào)用迭代器的next方法后,迭代器的索引會向后移動一位,指向第一個元素并將該元素返回,當(dāng)再次調(diào)用next方法時,迭代器的索引會指向第二個元素并將該元素返回,依此類推,直到hasNext方法返回false,表示到達(dá)了集合的末尾,終止對元素的遍歷。
第二章 數(shù)據(jù)結(jié)構(gòu)
2.1 數(shù)據(jù)結(jié)構(gòu)介紹
數(shù)據(jù)結(jié)構(gòu) : 數(shù)據(jù)用什么樣的方式組合在一起。
總結(jié)
數(shù)據(jù)結(jié)構(gòu):數(shù)據(jù)存儲的常用結(jié)構(gòu)
2.2 常見數(shù)據(jù)結(jié)構(gòu)
數(shù)據(jù)存儲的常用結(jié)構(gòu)有:棧、隊列、數(shù)組、鏈表和紅黑樹。我們分別來了解一下:
棧
- 棧:stack,又稱堆棧,它是運算受限的線性表,其限制是僅允許在標(biāo)的一端進(jìn)行插入和刪除操作,不允許在其他任何位置進(jìn)行添加、查找、刪除等操作。
簡單的說:采用該結(jié)構(gòu)的集合,對元素的存取有如下的特點
- 先進(jìn)后出(即,存進(jìn)去的元素,要在后它后面的元素依次取出后,才能取出該元素)。例如,子彈壓進(jìn)彈夾,先壓進(jìn)去的子彈在下面,后壓進(jìn)去的子彈在上面,當(dāng)開槍時,先彈出上面的子彈,然后才能彈出下面的子彈。
- 棧的入口、出口的都是棧的頂端位置。
這里兩個名詞需要注意:
- 壓棧:就是存元素。即,把元素存儲到棧的頂端位置,棧中已有元素依次向棧底方向移動一個位置。
- 彈棧:就是取元素。即,把棧的頂端位置元素取出,棧中已有元素依次向棧頂方向移動一個位置。
總結(jié)
棧就好比是瓶子里面裝東西,瓶子滿了,先出來的肯定是最后放進(jìn)去的
隊列
- 隊列:queue,簡稱隊,它同堆棧一樣,也是一種運算受限的線性表,其限制是僅允許在表的一端進(jìn)行插入,而在表的另一端進(jìn)行刪除。
簡單的說,采用該結(jié)構(gòu)的集合,對元素的存取有如下的特點:
- 先進(jìn)先出(即,存進(jìn)去的元素,要在后它前面的元素依次取出后,才能取出該元素)。例如,小火車過山洞,車頭先進(jìn)去,車尾后進(jìn)去;車頭先出來,車尾后出來。
- 隊列的入口、出口各占一側(cè)。例如,下圖中的左側(cè)為入口,右側(cè)為出口。
數(shù)組
- 數(shù)組:Array,是有序的元素序列,數(shù)組是在內(nèi)存中開辟一段連續(xù)的空間,并在此空間存放元素。就像是一排出租屋,有100個房間,從001到100每個房間都有固定編號,通過編號就可以快速找到租房子的人。
簡單的說,采用該結(jié)構(gòu)的集合,對元素的存取有如下的特點:
-
查找元素快:通過索引,可以快速訪問指定位置的元素
-
增刪元素慢
-
指定索引位置增加元素:需要創(chuàng)建一個新數(shù)組,將指定新元素存儲在指定索引位置,再把原數(shù)組元素根據(jù)索引,復(fù)制到新數(shù)組對應(yīng)索引的位置。如下圖
-
**指定索引位置刪除元素:**需要創(chuàng)建一個新數(shù)組,把原數(shù)組元素根據(jù)索引,復(fù)制到新數(shù)組對應(yīng)索引的位置,原數(shù)組中指定索引位置元素不復(fù)制到新數(shù)組中。如下圖
總結(jié)
數(shù)組:
數(shù)組從0開始計數(shù)的原因可能是能夠快速定位第n個位置的開頭
數(shù)組增刪慢,因為要擴容遷移元素,或者刪除也要遷移元素
鏈表
-
鏈表:linked list,由一系列結(jié)點node(鏈表中每一個元素稱為結(jié)點)組成,結(jié)點可以在運行時i動態(tài)生成。每個結(jié)點包括兩個部分:一個是存儲數(shù)據(jù)元素的數(shù)據(jù)域,另一個是存儲下一個結(jié)點地址的指針域。我們常說的鏈表結(jié)構(gòu)有單向鏈表與雙向鏈表,那么這里給大家介紹的是單向鏈表。
簡單的說,采用該結(jié)構(gòu)的集合,對元素的存取有如下的特點:
-
多個結(jié)點之間,通過地址進(jìn)行連接。例如,多個人手拉手,每個人使用自己的右手拉住下個人的左手,依次類推,這樣多個人就連在一起了。
-
查找元素慢:想查找某個元素,需要通過連接的節(jié)點,依次向后查找指定元素
-
增刪元素快:
總結(jié)
增刪不用遷移元素,只要改變元素地址的指向就好
改動的地方多,速度就慢,改動的地方少,速度就快
目標(biāo):常見的數(shù)據(jù)結(jié)構(gòu)種類。集合是基于數(shù)據(jù)結(jié)構(gòu)做出來的,不同的集合底層會采用不同的數(shù)據(jù)結(jié)構(gòu)。 不同的數(shù)據(jù)結(jié)構(gòu),功能和作用是不一樣的。什么是數(shù)據(jù)結(jié)構(gòu)?數(shù)據(jù)結(jié)構(gòu)指的是數(shù)據(jù)以什么方式組織在一起。不同的數(shù)據(jù)結(jié)構(gòu),增刪查的性能是不一樣的。不同的集合底層會采用不同的數(shù)據(jù)結(jié)構(gòu),我們要知道集合的底層是基于哪種數(shù)據(jù)結(jié)構(gòu)存儲和操作數(shù)據(jù)的。這樣才能知道具體場景用哪種集合。Java常見的數(shù)據(jù)結(jié)構(gòu)有哪些? 數(shù)據(jù)存儲的常用結(jié)構(gòu)有:棧、隊列、數(shù)組、鏈表和紅黑樹a.隊列(queue)-- 先進(jìn)先出,后進(jìn)后出。-- 場景:各種排隊。叫號系統(tǒng)。-- 有很多集合可以實現(xiàn)隊列。b.棧(stack)-- 后進(jìn)先出,先進(jìn)后出-- 壓棧 == 入棧-- 彈棧 == 出棧-- 場景:手槍的彈夾。c.數(shù)組-- 數(shù)組是內(nèi)存中的連續(xù)存儲區(qū)域。-- 分成若干等分的小區(qū)域(每個區(qū)域大小是一樣的)-- 元素存在索引-- 特點:查詢元素快(根據(jù)索引快速計算出元素的地址,然后立即去定位)增刪元素慢(創(chuàng)建新數(shù)組,遷移元素)d.鏈表-- 元素不是內(nèi)存中的連續(xù)區(qū)域存儲。-- 元素是游離存儲的。每個元素會記錄下個元素的地址。-- 特點:查詢元素慢增刪元素快(針對于首尾元素,速度極快,一般是雙鏈表)
2.3. 樹基本結(jié)構(gòu)介紹
樹具有的特點:
| 節(jié)點 | 指樹中的一個元素 |
| 節(jié)點的度 | 節(jié)點擁有的子樹的個數(shù),二叉樹的度不大于2 |
| 葉子節(jié)點 | 度為0的節(jié)點,也稱之為終端結(jié)點 |
| 高度 | 葉子結(jié)點的高度為1,葉子結(jié)點的父節(jié)點高度為2,以此類推,根節(jié)點的高度最高 |
| 層 | 根節(jié)點在第一層,以此類推 |
| 父節(jié)點 | 若一個節(jié)點含有子節(jié)點,則這個節(jié)點稱之為其子節(jié)點的父節(jié)點 |
| 子節(jié)點 | 子節(jié)點是父節(jié)點的下一層節(jié)點 |
| 兄弟節(jié)點 | 擁有共同父節(jié)點的節(jié)點互稱為兄弟節(jié)點 |
二叉樹
如果樹中的每個節(jié)點的子節(jié)點的個數(shù)不超過2,那么該樹就是一個二叉樹。
二叉查找樹/二叉排序樹
二叉查找樹的特點:
案例演示(20,18,23,22,17,24,19)數(shù)據(jù)的存儲過程;
增刪改查的性能都很高!!!
遍歷獲取元素的時候可以按照"左中右"的順序進(jìn)行遍歷;
注意:二叉查找樹存在的問題:會出現(xiàn)"瘸子"的現(xiàn)象,影響查詢效率。
總結(jié)
樹的進(jìn)化史
二叉查找樹:如果數(shù)據(jù)是已經(jīng)排序好,用二叉排序樹會形成單邊的情況,變成鏈表,使查找變得麻煩,使樹變高
樹是展示數(shù)據(jù)存儲和數(shù)據(jù)獲取的一種結(jié)構(gòu)
平衡二叉樹
(基于查找二叉樹,但是讓樹不要太高,盡量讓樹的元素均衡分布。這樣綜合性能就高了)
概述
為了避免出現(xiàn)"瘸子"的現(xiàn)象,減少樹的高度,提高我們的搜素效率,又存在一種樹的結(jié)構(gòu):“平衡二叉樹”
規(guī)則:它的左右兩個子樹的高度差的絕對值不超過1,并且左右兩個子樹都是一棵平衡二叉樹
如下圖所示:
如下圖所示,左圖是一棵平衡二叉樹,根節(jié)點10,左右兩子樹的高度差是1,而右圖,雖然根節(jié)點左右兩子樹高度差是0,但是右子樹15的左右子樹高度差為2,不符合定義,
所以右圖不是一棵平衡二叉樹。
總結(jié)
平衡二叉樹就是對二叉排序樹的瘸腿特性進(jìn)行優(yōu)化
左右兩個子樹的高度差的絕對值不超過1:
1與2的高度差不大于1(可以等于1),3與4的高度相等
旋轉(zhuǎn)
在構(gòu)建一棵平衡二叉樹的過程中,當(dāng)有新的節(jié)點要插入時,檢查是否因插入后而破壞了樹的平衡,如果是,則需要做旋轉(zhuǎn)去改變樹的結(jié)構(gòu)。
左旋:
左旋就是將節(jié)點的右支往左拉,右子節(jié)點變成父節(jié)點,并把晉升之后多余的左子節(jié)點出讓給降級節(jié)點的右子節(jié)點;
右旋:
將節(jié)點的左支往右拉,左子節(jié)點變成了父節(jié)點,并把晉升之后多余的右子節(jié)點出讓給降級節(jié)點的左子節(jié)點
舉個例子,像上圖是否平衡二叉樹的圖里面,左圖在沒插入前"19"節(jié)點前,該樹還是平衡二叉樹,但是在插入"19"后,導(dǎo)致了"15"的左右子樹失去了"平衡",
所以此時可以將"15"節(jié)點進(jìn)行左旋,讓"15"自身把節(jié)點出讓給"17"作為"17"的左樹,使得"17"節(jié)點左右子樹平衡,而"15"節(jié)點沒有子樹,左右也平衡了。如下圖,
由于在構(gòu)建平衡二叉樹的時候,當(dāng)有新節(jié)點插入時,都會判斷插入后時候平衡,這說明了插入新節(jié)點前,都是平衡的,也即高度差絕對值不會超過1。當(dāng)新節(jié)點插入后,
有可能會有導(dǎo)致樹不平衡,這時候就需要進(jìn)行調(diào)整,而可能出現(xiàn)的情況就有4種,分別稱作左左,左右,右左,右右。
左左
左左即為在原來平衡的二叉樹上,在節(jié)點的左子樹的左子樹下,有新節(jié)點插入,導(dǎo)致節(jié)點的左右子樹的高度差為2,如下即為"10"節(jié)點的左子樹"7",的左子樹"4",插入了節(jié)點"5"或"3"導(dǎo)致失衡。
左左調(diào)整其實比較簡單,只需要對節(jié)點進(jìn)行右旋即可,如下圖,對節(jié)點"10"進(jìn)行右旋,
左右
左右即為在原來平衡的二叉樹上,在節(jié)點的左子樹的右子樹下,有新節(jié)點插入,導(dǎo)致節(jié)點的左右子樹的高度差為2,如上即為"11"節(jié)點的左子樹"7",的右子樹"9",
插入了節(jié)點"10"或"8"導(dǎo)致失衡。
左右的調(diào)整就不能像左左一樣,進(jìn)行一次旋轉(zhuǎn)就完成調(diào)整。我們不妨先試著讓左右像左左一樣對"11"節(jié)點進(jìn)行右旋,結(jié)果圖如下,右圖的二叉樹依然不平衡,而右圖就是接下來要
講的右左,即左右跟右左互為鏡像,左左跟右右也互為鏡像。
左右這種情況,進(jìn)行一次旋轉(zhuǎn)是不能滿足我們的條件的,正確的調(diào)整方式是,將左右進(jìn)行第一次旋轉(zhuǎn),將左右先調(diào)整成左左,然后再對左左進(jìn)行調(diào)整,從而使得二叉樹平衡。
即先對上圖的節(jié)點"7"進(jìn)行左旋,使得二叉樹變成了左左,之后再對"11"節(jié)點進(jìn)行右旋,此時二叉樹就調(diào)整完成,如下圖:
右左
右左即為在原來平衡的二叉樹上,在節(jié)點的右子樹的左子樹下,有新節(jié)點插入,導(dǎo)致節(jié)點的左右子樹的高度差為2,如上即為"11"節(jié)點的右子樹"15",的左子樹"13",
插入了節(jié)點"12"或"14"導(dǎo)致失衡。
前面也說了,右左跟左右其實互為鏡像,所以調(diào)整過程就反過來,先對節(jié)點"15"進(jìn)行右旋,使得二叉樹變成右右,之后再對"11"節(jié)點進(jìn)行左旋,此時二叉樹就調(diào)整完成,如下圖:
右右
右右即為在原來平衡的二叉樹上,在節(jié)點的右子樹的右子樹下,有新節(jié)點插入,導(dǎo)致節(jié)點的左右子樹的高度差為2,如下即為"11"節(jié)點的右子樹"13",的左子樹"15",插入了節(jié)點
"14"或"19"導(dǎo)致失衡。
右右只需對節(jié)點進(jìn)行一次左旋即可調(diào)整平衡,如下圖,對"11"節(jié)點進(jìn)行左旋。
總結(jié)
右高(右樹)往左旋,左高(左樹)往右旋,把中間的節(jié)點提上去
不熟悉的東西先轉(zhuǎn)為熟悉的東西,再用熟悉的東西的方法來進(jìn)行解決
如果左高進(jìn)行右旋,還是不平衡,就放棄右旋,改為左旋,然后不平衡再用右高往左旋,左高往右旋,把中間的節(jié)點提上去
要提的是,不平衡的父節(jié)點,誰高就往矮的地方旋
紅黑樹
就是平衡的二叉查找樹!!
概述
紅黑樹是一種自平衡的二叉查找樹,是計算機科學(xué)中用到的一種數(shù)據(jù)結(jié)構(gòu),它是在1972年由Rudolf Bayer發(fā)明的,當(dāng)時被稱之為平衡二叉B樹,后來,在1978年被
Leoj.Guibas和Robert Sedgewick修改為如今的"紅黑樹"。它是一種特殊的二叉查找樹,紅黑樹的每一個節(jié)點上都有存儲位表示節(jié)點的顏色,可以是紅或者黑;
紅黑樹不是高度平衡的,它的平衡是通過"紅黑樹的特性"進(jìn)行實現(xiàn)的;
紅黑樹的特性:
如下圖所示就是一個
在進(jìn)行元素插入的時候,和之前一樣; 每一次插入完畢以后,使用黑色規(guī)則進(jìn)行校驗,如果不滿足紅黑規(guī)則,就需要通過變色,左旋和右旋來調(diào)整樹,使其滿足紅黑規(guī)則;
總結(jié)
紅黑樹就是紅黑交替
e.紅黑樹
二叉樹:binary tree 永遠(yuǎn)只有一個根節(jié)點,是每個結(jié)點不超過2個節(jié)點的樹(tree) 。
查找二叉樹,排序二叉樹:小的左邊,大的右邊,但是可能樹很高,性能變差。
為了做排序和搜索會進(jìn)行左旋和右旋實現(xiàn)平衡查找二叉樹,讓樹的高度差不大于1
紅黑樹(就是基于紅黑規(guī)則實現(xiàn)了自平衡的排序二叉樹):
樹盡量的保證到了很矮小,但是又排好序了,性能最高的樹。
紅黑樹的增刪查改性能都好!!!
這些結(jié)構(gòu),其實Java早就通過代碼實現(xiàn)了,我們要知道有這些結(jié)構(gòu)即可!
第三章 List接口
我們掌握了Collection接口的使用后,再來看看Collection接口中的子類,他們都具備那些特性呢?
接下來,我們一起學(xué)習(xí)Collection中的常用幾個子類(java.util.List集合、java.util.Set集合)。
3.1 List接口介紹
java.util.List接口繼承自Collection接口,是單列集合的一個重要分支,習(xí)慣性地會將實現(xiàn)了List接口的對象稱為List集合。在List集合中允許出現(xiàn)重復(fù)的元素,所有的元素是以一種線性方式進(jìn)行存儲的,在程序中可以通過索引來訪問集合中的指定元素。另外,List集合還有一個特點就是元素有序,即元素的存入順序和取出順序一致。
看完API,我們總結(jié)一下:
List接口特點:
tips:我們在基礎(chǔ)班的時候已經(jīng)學(xué)習(xí)過List接口的子類java.util.ArrayList類,該類中的方法都是來自List中定義。
3.2 List接口中常用方法
List作為Collection集合的子接口,不但繼承了Collection接口中的全部方法,而且還增加了一些根據(jù)元素索引來操作集合的特有方法,如下:
- public void add(int index, E element): 將指定的元素,添加到該集合中的指定位置上。
- public E get(int index):返回集合中指定位置的元素。
- public E remove(int index): 移除列表中指定位置的元素, 返回的是被移除的元素。
- public E set(int index, E element):用指定元素替換集合中指定位置的元素,返回值的更新前的元素。
List集合特有的方法都是跟索引相關(guān),我們在基礎(chǔ)班都學(xué)習(xí)過。
tips:我們之前學(xué)習(xí)Colletion體系的時候,發(fā)現(xiàn)List集合下有很多集合,它們的存儲結(jié)構(gòu)不同,這樣就導(dǎo)致了這些集合它們有各自的特點,供我們在不同的環(huán)境下使用,那么常見的數(shù)據(jù)結(jié)構(gòu)有哪些呢?在下一章我們來介紹:
3.3 ArrayList集合
java.util.ArrayList集合數(shù)據(jù)存儲的結(jié)構(gòu)是數(shù)組結(jié)構(gòu)。元素增刪慢,查找快,由于日常開發(fā)中使用最多的功能為查詢數(shù)據(jù)、遍歷數(shù)據(jù),所以ArrayList是最常用的集合。
許多程序員開發(fā)時非常隨意地使用ArrayList完成任何需求,并不嚴(yán)謹(jǐn),這種用法是不提倡的。
總結(jié)
目標(biāo):ArrayList集合。Collection集合的體系:Collection<E>(接口)/ \Set<E>(接口) List<E>(接口)/ \ / \ \HashSet<E>(實現(xiàn)類) TreeSet<E>(實現(xiàn)類) LinkedList<E>(實現(xiàn)類) Vector(線程安全) ArrayList<E>(實現(xiàn)類)/LinkedHashSet<E>(實現(xiàn)類)Collection集合體系的特點:Set系列集合: 添加的元素,是無序,不重復(fù),無索引的。-- HashSet:添加的元素,是無序,不重復(fù),無索引的。-- LinkedHashSet:添加的元素,是有序,不重復(fù),無索引的。List系列集合:添加的元素,是有序,可重復(fù),有索引的。-- LinkedList: 添加的元素,是有序,可重復(fù),有索引的。-- ArrayList: 添加的元素,是有序,可重復(fù),有索引的。-- Vector 是線程安全的,速度慢,工作中很少使用。List集合繼承了Collection集合的全部功能,同時因為List系列集合有索引,因為List集合多了索引,所以多了很多按照索引操作元素的功能:ArrayList實現(xiàn)類集合底層基于數(shù)組存儲數(shù)據(jù)的,查詢快,增刪慢!- public void add(int index, E element): 將指定的元素,添加到該集合中的指定位置上。- public E get(int index):返回集合中指定位置的元素。- public E remove(int index): 移除列表中指定位置的元素, 返回的是被移除的元素。- public E set(int index, E element):用指定元素替換集合中指定位置的元素,返回更新前的元素值。小結(jié):List系列集合有序,可重復(fù),有索引的。ArrayList實現(xiàn)類集合底層基于數(shù)組存儲數(shù)據(jù)的,查詢快,增刪慢!!開發(fā)中ArrayList集合用的最多!!List集合的遍歷方式擴展
總結(jié)
for循環(huán)是根據(jù)索引來進(jìn)行遍歷的
集合的長度為size
集合對象.get(索引)//可以用來去集合對象
快速創(chuàng)建迭代器對象:因為字符串變量無法接收迭代器,所以ide會報錯,利用alt+enter,可以快速修復(fù)錯誤,從而快速創(chuàng)建迭代器對象
/**拓展:List系列集合的遍歷方式有:4種。List系列集合多了索引,所以多了一種按照索引遍歷集合的for循環(huán)。List遍歷方式:(1)for循環(huán)。(2)迭代器。(3)foreach。(4)JDK 1.8新技術(shù)。*/ public class ListDemo02 {public static void main(String[] args) {List<String> lists = new ArrayList<>();lists.add("java1");lists.add("java2");lists.add("java3");/** (1)for循環(huán)。 */for(int i = 0 ; i < lists.size() ; i++ ) {String ele = lists.get(i);System.out.println(ele);}System.out.println("-----------------------");/** (2)迭代器。 */Iterator<String> it = lists.iterator();while(it.hasNext()){System.out.println(it.next());}System.out.println("-----------------------");/** (3)foreach。 */for(String ele : lists){System.out.println(ele);}System.out.println("-----------------------");/** (4)JDK 1.8開始之后的Lambda表達(dá)式*/lists.forEach(s -> {System.out.println(s);});} }
3.4 LinkedList集合
java.util.LinkedList集合數(shù)據(jù)存儲的結(jié)構(gòu)是鏈表結(jié)構(gòu)。方便元素添加、刪除的集合。
LinkedList是一個雙向鏈表,那么雙向鏈表是什么樣子的呢,我們用個圖了解下
實際開發(fā)中對一個集合元素的添加與刪除經(jīng)常涉及到首尾操作,而LinkedList提供了大量首尾操作的方法。這些方法我們作為了解即可:
- public void addFirst(E e):將指定元素插入此列表的開頭。
- public void addLast(E e):將指定元素添加到此列表的結(jié)尾。
- public E getFirst():返回此列表的第一個元素。
- public E getLast():返回此列表的最后一個元素。
- public E removeFirst():移除并返回此列表的第一個元素。
- public E removeLast():移除并返回此列表的最后一個元素。
- public E pop():從此列表所表示的堆棧處彈出一個元素。
- public void push(E e):將元素推入此列表所表示的堆棧。
- public boolean isEmpty():如果列表不包含元素,則返回true。
LinkedList是List的子類,List中的方法LinkedList都是可以使用,這里就不做詳細(xì)介紹,我們只需要了解LinkedList的特有方法即可。在開發(fā)時,LinkedList集合也可以作為堆棧,隊列的結(jié)構(gòu)使用。
public class Demo04LinkedList {public static void main(String[] args) {method4();}/** void push(E e): 壓入。把元素添加到集合的第一個位置。* E pop(): 彈出。把第一個元素刪除,然后返回這個元素。*/public static void method4() {//創(chuàng)建LinkedList對象LinkedList<String> list = new LinkedList<>();//添加元素list.add("達(dá)爾文");list.add("達(dá)芬奇");list.add("達(dá)爾優(yōu)");System.out.println("list:" + list);//調(diào)用push在集合的第一個位置添加元素//list.push("愛迪生");//System.out.println("list:" + list);//[愛迪生, 達(dá)爾文, 達(dá)芬奇, 達(dá)爾優(yōu)]//E pop(): 彈出。把第一個元素刪除,然后返回這個元素。String value = list.pop();System.out.println("value:" + value);//達(dá)爾文System.out.println("list:" + list);//[達(dá)芬奇,達(dá)爾優(yōu)]}/** E removeFirst():刪除第一個元素* E removeLast():刪除最后一個元素。*/public static void method3() {//創(chuàng)建LinkedList對象LinkedList<String> list = new LinkedList<>();//添加元素list.add("達(dá)爾文");list.add("達(dá)芬奇");list.add("達(dá)爾優(yōu)");//刪除集合的第一個元素 // String value = list.removeFirst(); // System.out.println("value:" + value);//達(dá)爾文 // System.out.println("list:" + list);//[達(dá)芬奇,達(dá)爾優(yōu)]//刪除最后一個元素String value = list.removeLast();System.out.println("value:" + value);//達(dá)爾優(yōu)System.out.println("list:" + list);//[達(dá)爾文, 達(dá)芬奇]}/** E getFirst(): 獲取集合中的第一個元素* E getLast(): 獲取集合中的最后一個元素*/public static void method2() {//創(chuàng)建LinkedList對象LinkedList<String> list = new LinkedList<>();//添加元素list.add("達(dá)爾文");list.add("達(dá)芬奇");list.add("達(dá)爾優(yōu)");System.out.println("list:" + list);//獲取集合中的第一個元素System.out.println("第一個元素是:" + list.getFirst());//獲取集合中的最后一個元素怒System.out.println("最后一個元素是:" + list.getLast());} /** void addFirst(E e): 在集合的開頭位置添加元素。* void addLast(E e): 在集合的尾部添加元素。*/public static void method1() {//創(chuàng)建LinkedList對象LinkedList<String> list = new LinkedList<>();//添加元素list.add("達(dá)爾文");list.add("達(dá)芬奇");list.add("達(dá)爾優(yōu)");//打印這個集合System.out.println("list:" + list);//[達(dá)爾文, 達(dá)芬奇, 達(dá)爾優(yōu)]//調(diào)用addFirst添加元素list.addFirst("曹操");System.out.println("list:" + list);//[曹操, 達(dá)爾文, 達(dá)芬奇, 達(dá)爾優(yōu)]//調(diào)用addLast方法添加元素list.addLast("大喬");System.out.println("list:" + list);//[曹操, 達(dá)爾文, 達(dá)芬奇, 達(dá)爾優(yōu), 大喬]} }總結(jié)
如果要用子類的特殊功能就不能用多態(tài)來進(jìn)行定義,因為用多態(tài)來進(jìn)行定義,變量是父類類型的,就不能調(diào)用子類的特殊功能
隊列是先進(jìn)先出,后進(jìn)后出
棧是先進(jìn)后出,后進(jìn)先出
LinkedList的push方法等同于addFirst方法
LinkedList的pop方法等同于removeFirst方法
目標(biāo):LinkedList集合。Collection集合的體系:Collection<E>(接口)/ \Set<E>(接口) List<E>(接口)/ / \ \HashSet<E>(實現(xiàn)類) LinkedList<E>(實現(xiàn)類) Vector(實現(xiàn)類) ArrayList<E>(實現(xiàn)類)/LinkedHashSet<E>(實現(xiàn)類)Collection集合體系的特點:Set系列集合: 添加的元素,是無序,不重復(fù),無索引的。-- HashSet:添加的元素,是無序,不重復(fù),無索引的。-- LinkedHashSet:添加的元素,是有序,不重復(fù),無索引的。List系列集合:添加的元素,是有序,可重復(fù),有索引的。-- LinkedList: 添加的元素,是有序,可重復(fù),有索引的。-- ArrayList: 添加的元素,是有序,可重復(fù),有索引的。LinkedList也是List的實現(xiàn)類:底層是基于鏈表的,增刪比較快,查詢慢!!LinkedList是支持雙鏈表,定位前后的元素是非常快的,增刪首尾的元素也是最快的所以LinkedList除了擁有List集合的全部功能還多了很多操作首尾元素的特殊功能:- public void addFirst(E e):將指定元素插入此列表的開頭。- public void addLast(E e):將指定元素添加到此列表的結(jié)尾。- public E getFirst():返回此列表的第一個元素。- public E getLast():返回此列表的最后一個元素。- public E removeFirst():移除并返回此列表的第一個元素。- public E removeLast():移除并返回此列表的最后一個元素。- public E pop():從此列表所表示的堆棧處彈出一個元素。- public void push(E e):將元素推入此列表所表示的堆棧。小結(jié):LinkedList是支持雙鏈表,定位前后的元素是非常快的,增刪首尾的元素也是最快的。所以提供了很多操作首尾元素的特殊API可以做棧和隊列的實現(xiàn)。如果查詢多而增刪少用ArrayList集合。(用的最多的)如果查詢少而增刪首尾較多用LinkedList集合。第四章 Set接口
java.util.Set接口和java.util.List接口一樣,同樣繼承自Collection接口,它與Collection接口中的方法基本一致,并沒有對Collection接口進(jìn)行功能上的擴充,只是比Collection接口更加嚴(yán)格了。與List接口不同的是,Set接口都會以某種規(guī)則保證存入的元素不出現(xiàn)重復(fù)。
Set集合有多個子類,這里我們介紹其中的java.util.HashSet、java.util.LinkedHashSet、java.util.TreeSet這兩個集合。
tips:Set集合取出元素的方式可以采用:迭代器、增強for。
4.1 HashSet集合介紹
java.util.HashSet是Set接口的一個實現(xiàn)類,它所存儲的元素是不可重復(fù)的,并且元素都是無序的(即存取順序不能保證不一致)。java.util.HashSet底層的實現(xiàn)其實是一個java.util.HashMap支持,由于我們暫時還未學(xué)習(xí),先做了解。
HashSet是根據(jù)對象的哈希值來確定元素在集合中的存儲位置,因此具有良好的存儲和查找性能。保證元素唯一性的方式依賴于:hashCode與equals方法。
我們先來使用一下Set集合存儲,看下現(xiàn)象,再進(jìn)行原理的講解:
public class HashSetDemo {public static void main(String[] args) {//創(chuàng)建 Set集合HashSet<String> set = new HashSet<String>();//添加元素set.add(new String("cba"));set.add("abc");set.add("bac"); set.add("cba"); //遍歷for (String name : set) {System.out.println(name);}} }輸出結(jié)果如下,說明集合中不能存儲重復(fù)元素:
cba abc bactips:根據(jù)結(jié)果我們發(fā)現(xiàn)字符串"cba"只存儲了一個,也就是說重復(fù)的元素set集合不存儲。
總結(jié)
目標(biāo):HashSet集合Collection集合的體系:Collection<E>(接口)/ \Set<E>(接口) List<E>(接口)/ \ / \ \HashSet<E>(實現(xiàn)類) TreeSet<E>(實現(xiàn)類) LinkedList<E>(實現(xiàn)類) Vector(線程安全) ArrayList<E>(實現(xiàn)類)/LinkedHashSet<E>(實現(xiàn)類)Collection集合體系的特點:Set系列集合: 添加的元素,是無序,不重復(fù),無索引的。-- HashSet:添加的元素,是無序,不重復(fù),無索引的。-- LinkedHashSet:添加的元素,是有序,不重復(fù),無索引的。-- TreeSet: 不重復(fù),無索引,按照大小默認(rèn)升序排序!! ( 可排序集合 )List系列集合:添加的元素,是有序,可重復(fù),有索引的。-- LinkedList: 添加的元素,是有序,可重復(fù),有索引的。底層是基于鏈表存儲數(shù)據(jù)的,查詢慢,增刪快-- ArrayList: 添加的元素,是有序,可重復(fù),有索引的。底層基于數(shù)組存儲數(shù)據(jù)的,查詢快,增刪慢研究兩個問題(面試熱點):1)Set集合添加的元素是不重復(fù)的,是如何去重復(fù)的?2)Set集合元素?zé)o序的原因是什么?4.2 HashSet集合存儲數(shù)據(jù)的結(jié)構(gòu)(哈希表)
什么是哈希表呢?
在JDK1.8之前,哈希表底層采用數(shù)組+鏈表實現(xiàn),即使用數(shù)組處理沖突,同一hash值的鏈表都存儲在一個數(shù)組里。但是當(dāng)位于一個桶中的元素較多,即hash值相等的元素較多時,通過key值依次查找的效率較低。而JDK1.8中,哈希表存儲采用數(shù)組+鏈表+紅黑樹實現(xiàn),當(dāng)鏈表長度超過閾值(8)時,將鏈表轉(zhuǎn)換為紅黑樹,這樣大大減少了查找時間。
簡單的來說,哈希表是由數(shù)組+鏈表+紅黑樹(JDK1.8增加了紅黑樹部分)實現(xiàn)的,如下圖所示。
看到這張圖就有人要問了,這個是怎么存儲的呢?
為了方便大家的理解我們結(jié)合一個存儲流程圖來說明一下:
總而言之,JDK1.8引入紅黑樹大程度優(yōu)化了HashMap的性能,那么對于我們來講保證HashSet集合元素的唯一,其實就是根據(jù)對象的hashCode和equals方法來決定的。如果我們往集合中存放自定義的對象,那么保證其唯一,就必須復(fù)寫hashCode和equals方法建立屬于當(dāng)前對象的比較方式。
總結(jié)
引用數(shù)據(jù)類型就是自定義類型
泛型中包括引用數(shù)據(jù)類型
即便是對象中值相同,不同對象new出來的地址自然不同
4.3 HashSet存儲自定義類型元素
給HashSet中存放自定義類型元素時,需要重寫對象中的hashCode和equals方法,建立自己的比較方式,才能保證HashSet集合中的對象唯一.
創(chuàng)建自定義Student類:
public class Student {private String name;private int age;//get/set@Overridepublic boolean equals(Object o) {if (this == o)return true;if (o == null || getClass() != o.getClass())return false;Student student = (Student) o;return age == student.age &&Objects.equals(name, student.name);}@Overridepublic int hashCode() {return Objects.hash(name, age);} }創(chuàng)建測試類:
public class HashSetDemo2 {public static void main(String[] args) {//創(chuàng)建集合對象 該集合中存儲 Student類型對象HashSet<Student> stuSet = new HashSet<Student>();//存儲 Student stu = new Student("于謙", 43);stuSet.add(stu);stuSet.add(new Student("郭德綱", 44));stuSet.add(new Student("于謙", 43));stuSet.add(new Student("郭麒麟", 23));stuSet.add(stu);for (Student stu2 : stuSet) {System.out.println(stu2);}} } 執(zhí)行結(jié)果: Student [name=郭德綱, age=44] Student [name=于謙, age=43] Student [name=郭麒麟, age=23]總結(jié)
對象.hashCode() 可以獲取對象的哈希值,相當(dāng)于是內(nèi)存地址
equals可以比較內(nèi)容,返回true代表重復(fù),如果返回false代表不重復(fù)
在空白處 右鍵 Generate 選擇equals() and hashCode()就可以自定義判斷l(xiāng)ist集合是否重復(fù)的方法
一直next 最后finish 即可
只要兩個對象的內(nèi)容一樣,equals比較的結(jié)果一定為true
如果要認(rèn)為內(nèi)容一樣,就代表重復(fù),讓list自動篩選重復(fù)元素,就選擇equals() and hashCode(),重寫hashCode()方法,令兩個對象的內(nèi)容一樣返回的哈希值也要一樣!true
兩個對象內(nèi)容一樣,返回的入?yún)⒕鸵粯?#xff0c;哈希值就一樣
目標(biāo):Set系列集合元素去重復(fù)的流程。
集合和泛型都只能支持引用數(shù)據(jù)類型。
1.對于有值特性的,Set集合可以直接判斷進(jìn)行去重復(fù)。
2.對于引用數(shù)據(jù)類型的類對象,Set集合是按照如下流程進(jìn)行是否重復(fù)的判斷。
Set集合會讓兩兩對象,先調(diào)用自己的hashCode()方法得到彼此的哈希值(所謂的內(nèi)存地址)
然后比較兩個對象的哈希值是否相同,如果不相同則直接認(rèn)為兩個對象不重復(fù)。
如果哈希值相同,會繼續(xù)讓兩個對象進(jìn)行equals比較內(nèi)容是否相同,如果相同認(rèn)為真的重復(fù)了
如果不相同認(rèn)為不重復(fù)。
Set集合會先讓對象調(diào)用hashCode()方法獲取兩個對象的哈希值比較
/
false true
/
不重復(fù) 繼續(xù)讓兩個對象進(jìn)行equals比較
/
false true
/
不重復(fù) 重復(fù)了
需求:只要對象內(nèi)容一樣,就希望集合認(rèn)為它們重復(fù)了。重寫hashCode和equals方法。
小結(jié):
如果希望Set集合認(rèn)為兩個對象只要內(nèi)容一樣就重復(fù)了,必須重寫對象的hashCode和equals方法。
set集合無序的原因
總結(jié)
因為哈希值是隨機的,所以取余的結(jié)果也是隨機的
數(shù)組都是存對象地址
如果取余的結(jié)果相同,就設(shè)置鏈,讓相同的位置的值指向這個值(就是鏈表)
為什么要用這種方法進(jìn)行存儲:因為哈希表的增刪改查性能都很好
如果在取余的值的位置上的數(shù)組找不到目標(biāo)值,就會找該位置的鏈表,一個一個查找
因為鏈表可能會略長,查詢速度會比較慢,所以就引入紅黑樹機制,將鏈表轉(zhuǎn)為紅黑樹,紅黑樹是以數(shù)值的大小進(jìn)行排列,集合就用哈希值作為數(shù)值來匹配紅黑樹
哈希表的底層是基于Node數(shù)組,每個元素都是一個節(jié)點;每一個節(jié)點會存自己的哈希值,自己的元素值,存自己指向的下一個元素的地址
看底層源碼可得,如果鏈表的長度大于等于8(從0開始,大于等于7),就轉(zhuǎn)為紅黑樹
目標(biāo):Set系列集合元素?zé)o序的根本原因。(面試必考)Set系列集合添加元素?zé)o序的根本原因是因為底層采用了哈希表存儲元素。JDK 1.8之前:哈希表 = 數(shù)組 + 鏈表 + (哈希算法) JDK 1.8之后:哈希表 = 數(shù)組 + 鏈表 + 紅黑樹 + (哈希算法)當(dāng)鏈表長度超過閾值(8)時,將鏈表轉(zhuǎn)換為紅黑樹,這樣大大減少了查找時間。小結(jié):Set系列集合是基于哈希表存儲數(shù)據(jù)的它的增刪改查的性能都很好!!但是它是無序不重復(fù)的!如果不在意當(dāng)然可以使用!4.4 LinkedHashSet
我們知道HashSet保證元素唯一,可是元素存放進(jìn)去是沒有順序的,那么我們要保證有序,怎么辦呢?
在HashSet下面有一個子類java.util.LinkedHashSet,它是鏈表和哈希表組合的一個數(shù)據(jù)存儲結(jié)構(gòu)。
演示代碼如下:
public class LinkedHashSetDemo {public static void main(String[] args) {Set<String> set = new LinkedHashSet<String>();set.add("bbb");set.add("aaa");set.add("abc");set.add("bbc");Iterator<String> it = set.iterator();while (it.hasNext()) {System.out.println(it.next());}} } 結(jié)果:bbbaaaabcbbc總結(jié)
有序指的是添加順序
LinkedHashSet顧名思義就是在HashSet的基礎(chǔ)上添加Linked(鏈)來記錄添加的順序
存數(shù)據(jù)依舊是按照set無序的方法存儲,但是取元素的時候,是根據(jù)鏈表開始取
目標(biāo):LinkedHashSet是HashSet的子類,元素是“有序” 不重復(fù),無索引.LinkedHashSet底層依然是使用哈希表存儲元素的, 但是每個元素都額外帶一個鏈來維護(hù)添加順序!! 不光增刪查快,還有序。缺點是多了一個存儲順序的鏈會占內(nèi)存空間!!而且不允許重復(fù),無索引。總結(jié):如果希望元素可以重復(fù),又有索引,查詢要快用ArrayList集合。(用的最多)如果希望元素可以重復(fù),又有索引,增刪要快要用LinkedList集合。(適合查詢元素比較少的情況,經(jīng)常要首尾操作元素的情況)如果希望增刪改查都很快,但是元素不重復(fù)以及無序無索引,那么用HashSet集合。如果希望增刪改查都很快且有序,但是元素不重復(fù)以及無索引,那么用LinkedHashSet集合。4.5 TreeSet集合
1. 特點
TreeSet集合是Set接口的一個實現(xiàn)類,底層依賴于TreeMap,是一種基于紅黑樹的實現(xiàn),其特點為:
進(jìn)行排序,具體取決于使用的構(gòu)造方法:
2. 演示
案例演示自然排序(20,18,23,22,17,24,19):
public static void main(String[] args) {//無參構(gòu)造,默認(rèn)使用元素的自然順序進(jìn)行排序TreeSet<Integer> set = new TreeSet<Integer>();set.add(20);set.add(18);set.add(23);set.add(22);set.add(17);set.add(24);set.add(19);System.out.println(set); }控制臺的輸出結(jié)果為: [17, 18, 19, 20, 22, 23, 24]案例演示比較器排序(20,18,23,22,17,24,19):
public static void main(String[] args) {//有參構(gòu)造,傳入比較器,使用比較器對元素進(jìn)行排序TreeSet<Integer> set = new TreeSet<Integer>(new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {//元素前 - 元素后 : 升序//元素后 - 元素前 : 降序return o2 - o1;}});set.add(20);set.add(18);set.add(23);set.add(22);set.add(17);set.add(24);set.add(19);System.out.println(set); }控制臺的輸出結(jié)果為: [24, 23, 22, 20, 19, 18, 17]總結(jié)
多態(tài)的優(yōu)點就是右邊的實現(xiàn)類可以多樣變化
有序是添加順序,排序是大小順序
目標(biāo):TreeSet集合。
TreeSet: 不重復(fù),無索引,按照大小默認(rèn)升序排序!!
TreeSet集合稱為排序不重復(fù)集合,可以對元素進(jìn)行默認(rèn)的升序排序。
TreeSet集合自自排序的方式:
1.有值特性的元素直接可以升序排序。(浮點型,整型)
2.字符串類型的元素會按照首字符的編號排序。
3.對于自定義的引用數(shù)據(jù)類型,TreeSet默認(rèn)無法排序,執(zhí)行的時候直接報錯,因為人家不知道排序規(guī)則。
自定義的引用數(shù)據(jù)類型的排序?qū)崿F(xiàn):
對于自定義的引用數(shù)據(jù)類型,TreeSet默認(rèn)無法排序
所以我們需要定制排序的大小規(guī)則,程序員定義大小規(guī)則的方案有2種:
a.直接為對象的類實現(xiàn)比較器規(guī)則接口Comparable,重寫比較方法(拓展方式)
上面比較也可簡化成一行代碼:return this.age - o.age;//升序,直接返回兩者差的結(jié)果
順著扣是升序,如果反著扣就是降序,例return o.age - this.age就是降序
b.直接為集合設(shè)置比較器Comparator對象,重寫比較方法
Set<Employee> employees1 = new TreeSet<>(new Comparator<Employee>() {@Overridepublic int compare(Employee o1, Employee o2) {// o1比較者 o2被比較者// 如果程序員認(rèn)為比較者大于被比較者 返回正數(shù)!// 如果程序員認(rèn)為比較者小于被比較者 返回負(fù)數(shù)!// 如果程序員認(rèn)為比較者等于被比較者 返回0!return o1.getAge() - o2.getAge();}});employees1.add(new Employee("播仔",6500.0,21));employees1.add(new Employee("播妞",7500.0,19));employees1.add(new Employee("喬治",4500.0,23));System.out.println(employees1);} }在集合中創(chuàng)建匿名內(nèi)部類來進(jìn)行比較,比較規(guī)則相同
注意:如果類和集合都帶有比較規(guī)則,優(yōu)先使用集合自帶的比較規(guī)則。小結(jié):TreeSet集合對自定義引用數(shù)據(jù)類型排序,默認(rèn)無法進(jìn)行。但是有兩種方式可以讓程序員定義大小規(guī)則:a.直接為對象的類實現(xiàn)比較器規(guī)則接口Comparable,重寫比較方法(拓展方式)b.直接為集合設(shè)置比較器Comparator對象,重寫比較方法注意:如果類和集合都帶有比較規(guī)則,優(yōu)先使用集合自帶的比較規(guī)則。
第五章 Collections類
5.1 Collections常用功能
-
java.utils.Collections是集合工具類,用來對集合進(jìn)行操作。
常用方法如下:
-
public static void shuffle(List<?> list):打亂集合順序。
-
public static <T> void sort(List<T> list):將集合中元素按照默認(rèn)規(guī)則排序。
-
public static <T> void sort(List<T> list,Comparator<? super T> ):將集合中元素按照指定規(guī)則排序。
代碼演示:
public class CollectionsDemo {public static void main(String[] args) {ArrayList<Integer> list = new ArrayList<Integer>();list.add(100);list.add(300);list.add(200);list.add(50);//排序方法 Collections.sort(list);System.out.println(list);} } 結(jié)果: [50,100, 200, 300]我們的集合按照默認(rèn)的自然順序進(jìn)行了排列,如果想要指定順序那該怎么辦呢?
總結(jié)
// 1.給集合批量添加元素 Collection<String> names = new ArrayList<>(); /*** 參數(shù)一:被添加元素的集合* 參數(shù)二:可變參數(shù),一批元素*/ Collections.addAll(names,"曹操","賈乃亮","王寶強","陳羽凡"); System.out.println(names);5.2 Comparator比較器
創(chuàng)建一個學(xué)生類,存儲到ArrayList集合中完成指定排序操作。
Student 類
public class Student{private String name;private int age;//構(gòu)造方法//get/set//toString }測試類:
public class Demo {public static void main(String[] args) {// 創(chuàng)建四個學(xué)生對象 存儲到集合中ArrayList<Student> list = new ArrayList<Student>();list.add(new Student("rose",18));list.add(new Student("jack",16));list.add(new Student("abc",20));Collections.sort(list, new Comparator<Student>() {@Overridepublic int compare(Student o1, Student o2) {return o1.getAge()-o2.getAge();//以學(xué)生的年齡升序}});for (Student student : list) {System.out.println(student);}} } Student{name='jack', age=16} Student{name='rose', age=18} Student{name='abc', age=20}總結(jié)
b.直接為集合設(shè)置比較器Comparator對象,重寫比較方法// 如果程序員認(rèn)為比較者大于被比較者 返回正數(shù)!// 如果程序員認(rèn)為比較者小于被比較者 返回負(fù)數(shù)!// 如果程序員認(rèn)為比較者等于被比較者 返回0! 注意:如果類和集合都帶有比較規(guī)則,優(yōu)先使用集合自帶的比較規(guī)則。5.3 可變參數(shù)
在JDK1.5之后,如果我們定義一個方法需要接受多個參數(shù),并且多個參數(shù)類型一致,我們可以對其簡化.
格式:
修飾符 返回值類型 方法名(參數(shù)類型... 形參名){ }代碼演示:
public class ChangeArgs {public static void main(String[] args) {int sum = getSum(6, 7, 2, 12, 2121);System.out.println(sum);}public static int getSum(int... arr) {int sum = 0;for (int a : arr) {sum += a;}return sum;} }注意:
? 1.一個方法只能有一個可變參數(shù)
? 2.如果方法中有多個參數(shù),可變參數(shù)要放到最后。
應(yīng)用場景: Collections
? 在Collections中也提供了添加一些元素方法:
? public static <T> boolean addAll(Collection<T> c, T... elements):往集合中添加一些元素。
代碼演示:
public class CollectionsDemo {public static void main(String[] args) {ArrayList<Integer> list = new ArrayList<Integer>();//原來寫法//list.add(12);//list.add(14);//list.add(15);//list.add(1000);//采用工具類 完成 往集合中添加元素 Collections.addAll(list, 5, 222, 1,2);System.out.println(list); }總結(jié)
目標(biāo):可變參數(shù)。可變參數(shù)用在形參中可以接收多個數(shù)據(jù)。 可變參數(shù)的格式:數(shù)據(jù)類型... 參數(shù)名稱可變參數(shù)的作用:傳輸參數(shù)非常靈活,方便。可以不傳輸參數(shù)。可以傳輸一個參數(shù)。可以傳輸多個參數(shù)。可以傳輸一個數(shù)組。可變參數(shù)在方法內(nèi)部本質(zhì)上就是一個數(shù)組。 可變參數(shù)的注意事項:1.一個形參列表中可變參數(shù)只能有一個!!2.可變參數(shù)必須放在形參列表的最后面!! 小結(jié):可變參數(shù)的作用:傳輸參數(shù)非常靈活,方便。可變參數(shù)的注意事項:1.一個形參列表中可變參數(shù)只能有一個!!2.可變參數(shù)必須放在形參列表的最后面!!第六章 集合綜合案例
6.1 案例介紹
按照斗地主的規(guī)則,完成洗牌發(fā)牌的動作。
具體規(guī)則:
使用54張牌打亂順序,三個玩家參與游戲,三人交替摸牌,每人17張牌,最后三張留作底牌。
6.2 案例分析
-
準(zhǔn)備牌:
牌可以設(shè)計為一個ArrayList,每個字符串為一張牌。
每張牌由花色數(shù)字兩部分組成,我們可以使用花色集合與數(shù)字集合嵌套迭代完成每張牌的組裝。
牌由Collections類的shuffle方法進(jìn)行隨機排序。 -
發(fā)牌
將每個人以及底牌設(shè)計為ArrayList,將最后3張牌直接存放于底牌,剩余牌通過對3取模依次發(fā)牌。
-
看牌
直接打印每個集合。
6.3 代碼實現(xiàn)
2 測試類
public class Demo12 {public static void main(String[] args) {// 創(chuàng)建一個ArrayList用于存放一副牌ArrayList<Poker> pokers = new ArrayList<>();pokers.add(new Poker("大王", ""));pokers.add(new Poker("小王", ""));String[] colors = new String[] {"?", "?", "?", "?"};String[] numbers = new String[] {"2", "A", "K", "Q", "J", "10", "9", "8", "7", "6", "5", "4", "3"};// 組合牌, 嵌套循環(huán)的流程:外循環(huán)一次,內(nèi)循環(huán)所有次// 2.使用嵌套循環(huán)生成一副牌for (String n : numbers) {// "2", "A"for (String c : colors) {// "?", "?", "?", "?"Poker p = new Poker(c, n);// 3.將54張牌放到集合pokers.add(p);}}// 打印 // System.out.println(pokers);// 洗牌: Collections,集合工具類// static void shuffle?(List<?> list) 將集合中元素的順序打亂Collections.shuffle(pokers);System.out.println("洗牌后:" + pokers);// 發(fā)牌// 1.創(chuàng)建3個玩家集合,創(chuàng)建底牌集合ArrayList<Poker> player01 = new ArrayList<>();ArrayList<Poker> player02 = new ArrayList<>();ArrayList<Poker> player03 = new ArrayList<>();ArrayList<Poker> diPai = new ArrayList<>();// 2.遍歷牌的集合// 0 1 2 3 4 5 6 7 8 9 10 ...51 52 53// pokers = [?5], [?4], [?8], [?A], [?7], [?2], [?6], [?J], [?A], [?7], [?6], [?5], [?7], [?10]// 玩家1: 索引0,3,6 索引 % 3 == 0// 玩家2: 索引1,4,7 索引 % 3 == 1// 玩家3: 索引2,5,8 索引 % 3 == 2// 3.根據(jù)索引將牌發(fā)給不同的玩家for (int i = 0; i < pokers.size(); i++) {// i表示索引,poker就是i索引對應(yīng)的pokerPoker poker = pokers.get(i);if (i >= 51) { // 最后3張給底牌diPai.add(poker);} else if (i % 3 == 0) { // 玩家1player01.add(poker);} else if (i % 3 == 1) { // 玩家2player02.add(poker);} else if (i % 3 == 2) { // 玩家3player03.add(poker);}}// 看牌System.out.println("玩家1: " + player01);System.out.println("玩家2: " + player02);System.out.println("玩家3: " + player03);System.out.println("底牌: " + diPai);// 還要創(chuàng)建一副牌// 創(chuàng)建一個ArrayList用于存放一副牌} }總結(jié)
如果String類型的特殊字符復(fù)制到idea會變成數(shù)字+字母,那就先復(fù)制特殊字符,再加引號
排序:
由輸出新牌觀察可得,可以根據(jù)牌的索引來判斷牌的大小。
大王也是相同的方法
總結(jié)
以上是生活随笔為你收集整理的java(五)-迭代器,数据结构,List,Set ,TreeSet集合,Collections工具类的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SecureCRT分屏显示
- 下一篇: 达尔优机械师合金版灯光