Java SE基础知识详解第[12]期—集合(Set、Collections、Map、集合嵌套)
寫在前面:
????????每一個不曾起舞的日子,都是對生命的辜負。
? ? ? ? 希望看到這里的每一個人都能努力學習,不負韶華,成就更好的自己。
????????以下僅是個人學習過程中的一些想法與感悟,Java知識博大精深,作為初學者,個人能力有限,哪里寫的不夠清楚、明白,還請各位不吝指正,歡迎交流與討論。如果有朋友因此了解了一些知識或?qū)ava有了更深層次的理解,從而進行更進一步的學習,那么這篇文章的意義也就達到了。
目錄
1.Set系列集合
1.1Set系列集系概述
1.2HashSet元素無序的底層原理:哈希表
1.3實現(xiàn)類:LinkedHashSet
1.4實現(xiàn)類:TreeSet
2.Collection體系的特點、使用場景總結(jié)
3.補充知識:可變參數(shù)
4.補充知識:集合工具類Collections
5.Collection體系的綜合案例
6.Map集合體系
6.1Map集合的概述
6.2Map集合體系特點
6.3Map集合常用API
6.4Map集合的遍歷方式一:鍵找值
6.5Map集合的遍歷方式二:鍵值對
6.6Map集合的遍歷方式三:lambda表達式
6.7Map集合的實現(xiàn)類HashMap、LinkedHashMap、TreeMap
6.7.1HashMap的特點和底層原理
6.7.2LinkedHashMap集合概述和特點
6.7.3TreeMap集合概述和特點
7.補充知識:集合的嵌套
集合(Set、Collections、Map、集合嵌套)
1.Set系列集合
1.1Set系列集系概述
Set系列集合特點
????????無序:存取順序不一致
????????不重復:可以去除重復
????????無索引:沒有帶索引的方法,所以不能使用普通for循環(huán)遍歷,也不能通過索引來獲取元素。
Set集合實現(xiàn)類特點
????????HashSet:無序、不重復、無索引。
????????LinkedHashSet:有序、不重復、無索引(并不是真的無索引,而是對外沒有提供索引相關API)。
????????TreeSet:排序(默認自動升序排序)、不重復、無索引。
????????Set集合的功能上基本上與Collection的API一致。
1.2HashSet元素無序的底層原理:哈希表
HashSet底層原理
????????HashSet集合底層采取哈希表存儲的數(shù)據(jù)。
????????哈希表是一種對于增刪改查數(shù)據(jù)性能都較好的結(jié)構(gòu)。
哈希表的組成
????????JDK8之前的,底層使用數(shù)組+鏈表組成
????????JDK8開始后,底層采用數(shù)組+鏈表+紅黑樹組成。
????????在了解哈希表之前需要先理解哈希值的概念。
哈希值
????????是JDK根據(jù)對象的地址,按照某種規(guī)則算出來的int類型的數(shù)值。
Object類的API
????????publicinthashCode():返回對象的哈希值
對象的哈希值特點
????????同一個對象多次調(diào)用hashCode()方法返回的哈希值是相同的
????????默認情況下,不同對象的哈希值是不同的。
哈希表的詳細流程
????????①創(chuàng)建一個默認長度16,默認加載因為0.75的數(shù)組,數(shù)組名table。
????????②根據(jù)元素的哈希值跟數(shù)組的長度求余計算出應存入的位置(哈希算法)。
????????③判斷當前位置是否為null,如果是null直接存入,如果位置不為null,表示有元素,則調(diào)用equals方法比較屬性值,如果一樣,則不存,如果不一樣,則存入數(shù)組。
JDK7新元素占老元素位置,指向老元素,JDK8中新元素掛在老元素下面。
????????④當數(shù)組存滿到16*0.75=12時,就自動擴容,每次擴容原先的兩倍。
JDK1.8版本開始HashSet原理解析
????????底層結(jié)構(gòu):哈希表(數(shù)組、鏈表、紅黑樹的結(jié)合體)
????????當掛在元素下面的數(shù)據(jù)過多時,查詢性能降低,從JDK8開始后,當鏈表長度超過8的時候,自動轉(zhuǎn)換為紅黑樹,進一步提高了操作數(shù)據(jù)的性能。
案例:Set集合去重復
????????需求:創(chuàng)建一個存儲學生對象的集合,存儲多個學生對象,使用程序?qū)崿F(xiàn)在控制臺遍歷該集合,要求:學生對象的成員變量值相同,我們就認為是同一個對象
分析:
????????①定義學生類,創(chuàng)建HashSet集合對象,創(chuàng)建學生對象。
????????②把學生添加到集合。
????????③在學生類中重寫兩個方法,hashCode()和equals(),自動生成即可。
????????④遍歷集合(增強for)。
????????示例代碼如下:
Student類
public class Student {private String name;private int age;private char sex;public Student() {}public Student(String name, int age, char sex) {this.name = name;this.age = age;this.sex = sex;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public char getSex() {return sex;}public void setSex(char sex) {this.sex = sex;}/*** 兩個對象中每個字段內(nèi)容相同即返回true** @param o* @return*/@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == nul|| getClass() != o.getClass()) return false;Student student = (Student) o;return age == student.age &&sex == student.sex &&Objects.equals(name, student.name);}@Overridepublic int hashCode() {return Objects.hash(name, age, sex);}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", sex=" + sex +'}';} }測試類
public class SetDemo2 {public static void main(String[] args) {/*需求:創(chuàng)建一個存儲學生對象的集合,存儲多個學生對象,使用程序?qū)崿F(xiàn)在控制臺遍歷該集合要求:學生對象的成員變量值相同,我們就認為是同一個對象*/Set<Student> sets = new HashSet<>();sets.add(new Student("張三", 18, '男'));sets.add(new Student("李四", 20, '男'));sets.add(new Student("李四", 20, '男'));System.out.println(sets); // [Student{name='李四', age=20, sex=男}, Student{name='張三', age=18, sex=男}]// Set集合去重復原理:先判斷哈希值,再判斷equals// 若沒有重寫hasCode()與equals()方法,打印結(jié)果為[Student{name='李四', age=20, sex=男}, Student{name='張三', age=18, sex=男}, Student{name='李四', age=20, sex=男}]// 沒有去重復,兩個對象地址不同,哈希值就不同,存儲位置也就不同} }????????自動生成重寫hasCode()與equals()方法步驟如下。
????????①如下圖所示,在Student類中空白處點擊【鼠標右鍵】,選擇【生成】。
????????②如下圖所示,在彈出的頁面中選擇【equals()和hasCode()】。
????????③在彈出的頁面中依次選擇【下一個】,最后選擇【完成】,如下圖所示,即可自動生成重寫的equals()與hasCode()方法。
?
1.3實現(xiàn)類:LinkedHashSet
LinkedHashSet集合概述和特點
????????有序、不重復、無索引。
????????這里的有序指的是保證存儲和取出的元素順序一致。
????????原理:底層數(shù)據(jù)結(jié)構(gòu)依然是哈希表,只是每個元素又額外的多了一個雙鏈表記錄存儲順序。
1.4實現(xiàn)類:TreeSet
TreeSet集合概述和特點
????????不重復、無索引、可排序。
????????可排序:按照元素的大小默認升序(從小到大)排序。
????????TreeSet集合底層是基于紅黑樹的數(shù)據(jù)結(jié)構(gòu)實現(xiàn)排序的,增刪改查性能都較好。
????????注意:TreeSet集合是一定要排序的,可以將元素按照指定的規(guī)則進行排序。
TreeSet集合默認的規(guī)則
????????對于數(shù)值類型:Integer , Double,官方默認按照大小進行升序排序。
????????對于字符串類型:默認按照首字符的編號升序排序。
????????對于自定義類型如Student對象,TreeSet無法直接排序。
????????注:想要使用TreeSet存儲自定義類型,需要制定排序規(guī)則。
自定義排序規(guī)則
????????TreeSet集合存儲對象的的時候有2種方式可以設計自定義比較規(guī)則。
????????方式一:讓自定義類(如學生類)實現(xiàn)Comparable接口重寫里面的compareTo方法來制定比較規(guī)則。
????????方式二:TreeSet集合有參數(shù)構(gòu)造器,可以設置Comparator接口對應的比較器對象,來制定比較規(guī)則。
關于返回值的規(guī)則:
????????如果認為第一個元素大于第二個元素返回正整數(shù)即可。
????????如果認為第一個元素小于第二個元素返回負整數(shù)即可。
????????如果認為第一個元素等于第二個元素返回0即可,此時Treeset集合只會保留一個元素,認為兩者重復。
????????示例代碼如下:
Apple類
public class Apple implements Comparable<Apple> {private String name;private String color;private double price;private int weight;public Apple() {}public Apple(String name, String color, double price, int weight) {this.name = name;this.color = color;this.price = price;this.weight = weight;}@Overridepublic String toString() {return "Apple{" +"name='" + name + '\'' +", color='" + color + '\'' +", price=" + price +", weight=" + weight +'}';}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getColor() {return color;}public void setColor(String color) {this.color = color;}public double getPrice() {return price;}public void setPrice(double price) {this.price = price;}public int getWeight() {return weight;}public void setWeight(int weight) {this.weight = weight;}/*** 方式一:類自定義比較規(guī)則** @param o* @return*/@Overridepublic int compareTo(Apple o) {// 按照重量從小到大排序 // return this.weight - o.weight; // "金元帥"與"金冠"的"weight"屬性都為500,TreeSet集合在排序時認為二者重復,會刪去一個return this.weight - o.weight >= 0 ? 1 : -1; // 去掉二者相等返回0的情況,保證"weight"相等的數(shù)據(jù)都會得到保留}}測試類
public class SetDemo3 {public static void main(String[] args) {// 存儲數(shù)值類型:Integer為例Set<Integer> sets = new TreeSet<>(); // 不重復 無索引 可排序sets.add(10);sets.add(23);sets.add(7);System.out.println(sets); // [7, 10, 23]// 存儲字符串類型:StringSet<String> sets2 = new TreeSet<>();sets2.add("Java");sets2.add("java");sets2.add("李四");sets2.add("HTML");sets2.add("Zed");System.out.println(sets2); // [HTML, Java, Zed, java, 李四]// 存儲自定義類型:Student為例Set<Apple> sets3 = new TreeSet<>();sets3.add(new Apple("紅富士", "紅色", 9.9, 500));sets3.add(new Apple("青蘋果", "青色", 12.5, 300));sets3.add(new Apple("金元帥", "金色", 20, 600));sets3.add(new Apple("金冠", "金色", 25.5, 500));// 如果沒有制定排序規(guī)則直接打印集合,則無法排序,會報錯System.out.println(sets3);// [Apple{name='青蘋果', color='青色', price=12.5, weight=300}, Apple{name='紅富士', color='紅色', price=9.9, weight=500},// Apple{name='金冠', color='金色', price=25.5, weight=500}, Apple{name='金元帥', color='金色', price=20.0, weight=600}]System.out.println("-----------"); /* Set<Apple> apples = new TreeSet<>(new Comparator<Apple>() {// 方式二:集合自帶比較器對象進行規(guī)則制定// 注:如果TreeSet集合存儲的對象有實現(xiàn)比較規(guī)則,集合也自帶比較器,默認使用集合自帶的比較器排序。@Overridepublic int compare(Apple o1, Apple o2) { // return o1.getWeight() - o2.getWeight(); // "金元帥"與"金冠"的"weight"屬性都為500,TreeSet集合在排序時認為二者重復,會刪去一個return o1.getWeight() - o2.getWeight() >= 0 ? 1 : -1; // 去掉二者相等返回0的情況,保證"weight"相等的數(shù)據(jù)都會得到保留}});*/// lambda表達式簡化Set<Apple> apples = new TreeSet<>((o1, o2) -> o1.getWeight() - o2.getWeight() >= 0 ? 1 : -1);apples.add(new Apple("紅富士", "紅色", 9.9, 500));apples.add(new Apple("青蘋果", "青色", 12.5, 300));apples.add(new Apple("金元帥", "金色", 20, 600));apples.add(new Apple("金冠", "金色", 25.5, 500));System.out.println(apples);// [Apple{name='青蘋果', color='青色', price=12.5, weight=300}, Apple{name='紅富士', color='紅色', price=9.9, weight=500},// Apple{name='金冠', color='金色', price=25.5, weight=500}, Apple{name='金元帥', color='金色', price=20.0, weight=600}]} }????????注:如果TreeSet集合存儲的對象有實現(xiàn)比較規(guī)則,集合也自帶比較器,默認使用集合自帶的比較器排序。
2.Collection體系的特點、使用場景總結(jié)
1.如果希望元素可以重復,又有索引,索引查詢要快?
????????用ArrayList集合,基于數(shù)組的。(用的最多)
2.如果希望元素可以重復,又有索引,增刪首尾操作快?
????????用LinkedList集合,基于鏈表的。
3.如果希望增刪改查都快,但是元素不重復、無序、無索引。
????????用HashSet集合,基于哈希表的。
4.如果希望增刪改查都快,但是元素不重復、有序、無索引。
????????用LinkedHashSet集合,基于哈希表和雙鏈表。
5.如果要對對象進行排序。
????????用TreeSet集合,基于紅黑樹。后續(xù)也可以用List集合實現(xiàn)排序。
3.補充知識:可變參數(shù)
可變參數(shù)概述
????????可變參數(shù)用在形參中可以接收多個數(shù)據(jù)。
????????可變參數(shù)的格式:數(shù)據(jù)類型...參數(shù)名稱
可變參數(shù)的作用
????????傳輸參數(shù)非常靈活,方便。可以不傳輸參數(shù),可以傳輸1個或者多個,也可以傳輸一個數(shù)組
????????可變參數(shù)在方法內(nèi)部本質(zhì)上就是一個數(shù)組。
可變參數(shù)的注意事項:
????????1.一個形參列表中可變參數(shù)只能有一個。
????????2.可變參數(shù)必須放在形參列表的最后面。
????????示例代碼如下:
public class MethodDemo {public static void main(String[] args) {// 1.不傳參數(shù)sum();// 2.傳輸1個參數(shù)sum(10);// 3.傳輸多個參數(shù)sum(10, 20, 50);// 4.傳輸數(shù)組sum(new int[]{10, 20, 30});}public static void sum(int... nums) {// 可變參數(shù)在方法內(nèi)部實際上就是一個數(shù)組System.out.println("元素個數(shù):" + nums.length);System.out.println("元素內(nèi)容:" + Arrays.toString(nums));System.out.println("-----------");} }????????程序運行結(jié)果如下:
元素個數(shù):0
元素內(nèi)容:[]
-----------
元素個數(shù):1
元素內(nèi)容:[10]
-----------
元素個數(shù):3
元素內(nèi)容:[10, 20, 50]
-----------
元素個數(shù):3
元素內(nèi)容:[10, 20, 30]
-----------
4.補充知識:集合工具類Collections
Collections集合工具類概述
????????java.utils.Collections:是集合工具類
????????作用:Collections并不屬于集合,是用來操作集合的工具類。
Collections常用的API
| 方法名 | 說明 |
| public static <T> boolean addAll(Collection<? super T> c, T... elements) | 給集合對象批量添加元素 |
| public static void shuffle(List<?> list) | 打亂List集合元素的順序 |
Collections排序相關API
????????使用范圍:只能對于List集合的排序。
????????注:Shift + F6 一鍵修改所有出現(xiàn)的該參數(shù)名稱
具有值特性的類型對象排序
| 方法名 | 說明 |
| public static <T> void sort(List<T> list) | 將集合中(具有值特性的)元素按照默認規(guī)則(從小到大)排序 |
????????注意:本方式不可以直接對自定義類型的List集合排序,除非自定義類型實現(xiàn)了比較規(guī)則Comparable接口。
自定義類型對象排序
| 方法名 | 說明 |
| public static <T> void sort(List<T> list,Comparator<? super T> c) | 將集合中元素按照指定規(guī)則排序 |
| 在自定義類型中實現(xiàn)比較規(guī)則Comparable接口 |
????????示例代碼如下:
Apple類
public class Apple implements Comparable<Apple> {private String name;private String color;private double price;private int weight;public Apple() {}public Apple(String name, String color, double price, int weight) {this.name = name;this.color = color;this.price = price;this.weight = weight;}@Overridepublic String toString() {return "Apple{" +"name='" + name + '\'' +", color='" + color + '\'' +", price=" + price +", weight=" + weight +'}';}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getColor() {return color;}public void setColor(String color) {this.color = color;}public double getPrice() {return price;}public void setPrice(double price) {this.price = price;}public int getWeight() {return weight;}public void setWeight(int weight) {this.weight = weight;}/*** 方式一:類自定義比較規(guī)則** @param o* @return*/@Overridepublic int compareTo(Apple o) {// 按照重量從小到大排序return this.weight - o.weight; // List集合允許重復,不會刪去重復元素,即相等也無妨} }測試類
public class CollectionsDemo1 {public static void main(String[] args) {List<String> names = new ArrayList<>();// 1. public static <T> boolean addAll(Collection<? super T> c, T... elements) 給集合對象批量添加元素 // names.add("張三"); // names.add("李四"); // names.add("王五");// 添加數(shù)據(jù)比較麻煩,使用addAll方法批量添加數(shù)據(jù)Collections.addAll(names, "張三", "李四", "王五");System.out.println(names); // [張三, 李四, 王五]// 2. public static void shuffle(List<?> list) 打亂List集合元素的順序Collections.shuffle(names);System.out.println(names); // [李四, 王五, 張三]// 3. public static <T> void sort(List<T> list) 將集合中(具有值特性的)元素按照默認規(guī)則(從小到大)排序List<Integer> list = new ArrayList<>();Collections.addAll(list, 2, 5, 19, 10, 3);Collections.sort(list);System.out.println(list); // [2, 3, 5, 10, 19]// 自定義類型對象排序// ①在自定義類型中實現(xiàn)比較規(guī)則Comparable接口List<Apple> apples = new ArrayList<>();apples.add(new Apple("紅富士", "紅色", 9.9, 500));apples.add(new Apple("青蘋果", "青色", 12.5, 300));apples.add(new Apple("金元帥", "金色", 20, 600));apples.add(new Apple("金冠", "金色", 25.5, 500));Collections.sort(apples);System.out.println(apples);// [Apple{name='青蘋果', color='青色', price=12.5, weight=300}, Apple{name='紅富士', color='紅色', price=9.9, weight=500},// Apple{name='金冠', color='金色', price=25.5, weight=500}, Apple{name='金元帥', color='金色', price=20.0, weight=600}]// ②通過重寫比較器對象中的比較方法,自定義比較規(guī)則// 4. public static <T> void sort(List<T> list,Comparator<? super T> c) 將集合中元素按照指定規(guī)則排序 // Collections.sort(apples, new Comparator<Apple>() { // @Override // public int compare(Apple o1, Apple o2) { // // 根據(jù)價格升序排序 // return Double.compare(o1.getPrice(), o2.getPrice()); // public static int compare(double d1, double d2)方法比較兩個double類型數(shù)據(jù),返回1或-1 // } // });// 使用lambda表達式簡化匿名內(nèi)部類的寫法Collections.sort(apples, (o1, o2) -> Double.compare(o1.getPrice(), o2.getPrice())); // public static int compare(double d1, double d2)方法比較兩個double類型數(shù)據(jù),返回1或-1System.out.println(apples); // 重寫比較器對象中的比較方法方式優(yōu)先級高于自定義類型中實現(xiàn)比較規(guī)則Comparable接口,優(yōu)先匹配重寫比較器對象中的比較方法方式// [Apple{name='紅富士', color='紅色', price=9.9, weight=500}, Apple{name='青蘋果', color='青色', price=12.5, weight=300},// Apple{name='金元帥', color='金色', price=20.0, weight=600}, Apple{name='金冠', color='金色', price=25.5, weight=500}]} }????????注:重寫比較器對象中的比較方法方式優(yōu)先級高于自定義類型中實現(xiàn)比較規(guī)則Comparable接口,優(yōu)先匹配重寫比較器對象中的比較方法方式。
5.Collection體系的綜合案例
案例—斗地主游戲
????????需求:在啟動游戲房間的時候,應該提前準備好54張牌,完成洗牌、發(fā)牌、牌排序、邏輯。
分析:
????????①:當系統(tǒng)啟動的同時需要準備好數(shù)據(jù),可以使用靜態(tài)代碼塊來完成。
????????②:洗牌就是打亂牌的順序。
????????③:定義三個玩家、依次發(fā)出51張牌。
????????④:給玩家的牌進行排序(拓展)。
????????⑤:輸出每個玩家的牌數(shù)據(jù)。
????????示例代碼如下:
Card類
public class Card {private String size; // 點數(shù)private String color; // 花色private int index; // 撲克牌權(quán)值大小public Card() {}public Card(String size, String color, int index) {this.size = size;this.color = color;this.index = index;}@Overridepublic String toString() {return color + size;}// getter、setter }測試類
public class GameDemo {// 定義一個靜態(tài)的集合存儲54張牌public static List<Card> allCards = new ArrayList<>();static int index; // 記錄撲克牌權(quán)值大小// 定義靜態(tài)代碼塊初始化牌的數(shù)據(jù)static {// 初始化數(shù)據(jù):點數(shù)確定、類型確定,使用數(shù)組String[] sizes = {"3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "A", "2"};// 初始化數(shù)據(jù):花色數(shù)目確定、類型確定,使用數(shù)組String[] colors = {"?", "?", "?", "?"};for (String color : colors) {index = 0; // 表示sizes數(shù)組中的"3"權(quán)值最小,僅表示大小關系,不一定是0,其他數(shù)目也可以for (String size : sizes) {index++; // 每向后查詢一次,撲克牌權(quán)值大小+1,代表具有更大的效力// 封裝成一個牌對象Card card = new Card(size, color, index);// 將牌對象封裝到集合容器中allCards.add(card);}}// 此時index代表數(shù)字中效力最大的"2"的權(quán)值,大王>小王>2// 將大、小王牌對象封裝到集合容器中allCards.add(new Card("", "小王", ++index));allCards.add(new Card("", "大王", ++index));System.out.println("新牌:" + allCards);}public static void main(String[] args) {// 洗牌—打亂撲克牌順序Collections.shuffle(allCards);System.out.println("洗牌后:" + allCards);// 定義3個ArrayList集合容器,代表3位玩家手中的撲克牌List<Card> cardList1 = new ArrayList<>();List<Card> cardList2 = new ArrayList<>();List<Card> cardList3 = new ArrayList<>();// 發(fā)牌—在牌堆中分別給3位玩家發(fā)17張牌,剩余3張作為底牌for (int i = 0; i < allCards.size() - 3; i++) {Card card = allCards.get(i);// 輪循:第n張牌發(fā)給第1位玩家;第n + 1張牌發(fā)給第2位玩家;第n + 3張牌發(fā)給第3位玩家// 通過取余的方式實現(xiàn)switch (i % 3) {case 0:cardList1.add(card);break;case 1:cardList2.add(card);break;case 2:cardList3.add(card);break;default:System.out.println("系統(tǒng)錯誤!");}}// 拿到3張底牌// 通過 List<E> subList(int fromIndex, int toIndex)方法(左閉右開)把最后3張牌截取為子集合List<Card> lastThreeCards = allCards.subList(allCards.size() - 3, allCards.size());// 模擬搶地主Scanner sc = new Scanner(System.in);System.out.println("請輸入1、2、3第幾位玩家是地主:");int landlord = sc.nextInt();// 為地主玩家分配底牌switch (landlord) {case 1:cardList1.addAll(lastThreeCards);break;case 2:cardList2.addAll(lastThreeCards);break;case 3:cardList3.addAll(lastThreeCards);break;default:System.out.println("系統(tǒng)錯誤!");}// 給玩家手牌排序sortCards(cardList1);sortCards(cardList2);sortCards(cardList3);// 將玩家手牌輸出System.out.println("玩家1:" + cardList1);System.out.println("玩家2:" + cardList2);System.out.println("玩家3:" + cardList3);System.out.println("底牌:" + lastThreeCards);}/*** create by: 全聚德在逃烤鴨、* description: 撲克牌排序* create time: 2022/5/6 0006 16:23** @param cardList* @return void*/private static void sortCards(List<Card> cardList) {// Collections工具類的 public static <T> void sort(List<T> list, Comparator<? super T> c)方法與List類中的default void sort(Comparator<? super E> c)方法完全相同Collections.sort(cardList, (o1, o2) -> o2.getIndex() - o1.getIndex()); // 降序排列} }????????注:Collections工具類的 public static <T> void sort(List<T> list, Comparator<? super T> c)方法與List類中的default void sort(Comparator<? super E> c)方法完全相同
6.Map集合體系
6.1Map集合的概述
Map集合概述和使用
????????Collection是單列集合體系,Map集合是一種雙列集合,每個元素包含兩個數(shù)據(jù)。
????????Map集合的每個元素的格式:key=value(鍵值對元素)。
????????Map集合也被稱為“鍵值對集合”。
Map集合整體格式:
????????Collection集合的格式:[元素1,元素2,元素3..]
????????Map集合的完整格式:{key1=value1 , key2=value2 , key3=value3 , ...}
6.2Map集合體系特點
????????Map集合體系如下圖所示。
????????使用最多的Map集合是HashMap,重點掌握HashMap , LinkedHashMap , TreeMap。其他的后續(xù)理解。
Map集合體系特點
????????Map集合的特點都是由鍵決定的。
????????Map集合的鍵是無序,不重復,無索引,值不做要求(可以重復)。
????????Map集合后面重復的鍵對應的值會覆蓋前面重復鍵的值。
????????Map集合的鍵和值均可以為null。
Map集合實現(xiàn)類特點
????????HashMap:元素按照鍵是無序,不重復,無索引,值不做要求。(與Map體系一致)
????????LinkedHashMap:元素按照鍵是有序,不重復,無索引,值不做要求。
????????TreeMap:元素按照建是排序,不重復,無索引的,值不做要求。
????????示例代碼如下:
public static void main(String[] args) {Map<String, Integer> maps = new HashMap<>();maps.put("Java", 1);maps.put("Nike", 5);maps.put("BENZ", 3);maps.put("Java", 2); // 鍵"Java"中的值"2"會覆蓋之前的值"1"maps.put("Dell", 3);maps.put(null, null); // 鍵和值均支持nullSystem.out.println(maps); // {Nike=5, BENZ=3, null=null, Java=2, Dell=3}}6.3Map集合常用API
Map集合
????????Map是雙列集合的祖宗接口,它的功能是全部雙列集合都可以繼承使用的。
Map集合常用API
| 方法名 | 說明 |
| V put(K key,V value) | 根據(jù)鍵添加元素值,返回該鍵對應的原值, 若沒有原值(添加新元素)則返回null |
| V get(Object key) | 根據(jù)鍵獲取對應值 |
| V remove(Object key) | 根據(jù)鍵刪除鍵值對元素, 返回被刪除鍵對應的值 |
| void clear() | 清空集合 |
| boolean containsKey(Object key) | 判斷集合是否包含指定的鍵 |
| boolean containsValue(Object value) | 判斷集合是否包含指定的值 |
| boolean isEmpty() | 判斷集合是否為空 |
| int size() | 集合的長度,也就是集合中鍵值對的個數(shù) |
| Set<K> keySet() | 獲取全部鍵的集合 |
| Collection<V> values() | 獲取全部值的集合,不會刪去重復的值 |
| void putAll(Map<? extends K, ? extends V> m) | 拷貝其他map集合,后面重復的鍵對應的值會覆蓋前面重復鍵的值 |
????????示例代碼如下:
??
public static void main(String[] args) {Map<String, Integer> maps = new HashMap<>();maps.put("iphoneX", 10);maps.put("娃娃", 20);maps.put("iphoneX", 100); // 鍵"iphoneX"中的值"100"會覆蓋之前的值"10"maps.put("HUAWEI", 100);maps.put("生活用品", 10);maps.put("手表", 10);// {手表=10, 生活用品=10, iphoneX=100, 娃娃=20, HUAWEI=100}System.out.println(maps);// 1. void clear() 清空集合 // maps.clear(); // System.out.println(maps); // {}// 2. boolean isEmpty() 判斷集合是否為空 // boolean isEmpty = maps.isEmpty(); // System.out.println(isEmpty); // true// 3. V get(Object key) 根據(jù)鍵獲取對應值Integer value = maps.get("HUAWEI");System.out.println(value); // 100System.out.println(maps.get("Nike")); // nu 若沒有該鍵,取出的值為"null"// 4. V remove(Object key) 根據(jù)鍵刪除鍵值對元素,返回被刪除鍵對應的值Integer value2 = maps.remove("iphoneX");System.out.println(value2); // 100// 5. boolean containsKey(Object key) 判斷集合是否包含指定的鍵boolean isContainsKey = maps.containsKey("手表");System.out.println(isContainsKey); // trueSystem.out.println(maps.containsKey("iphoneX")); // false// 6. boolean containsValue(Object value) 判斷集合是否包含指定的值boolean isContainsValue = maps.containsValue(100);System.out.println(isContainsValue); // trueSystem.out.println(maps.containsValue(50)); // false// 7. Set<K> keySet() 獲取全部鍵的集合Set<String> keys = maps.keySet();System.out.println(keys); // [手表, 生活用品, 娃娃, HUAWEI]// 8. Collection<V> values() 獲取全部值的集合Collection<Integer> values = maps.values();System.out.println(values); // [10, 10, 20, 100] 不會刪去重復的值// 9. int size() 集合的長度,也就是集合中鍵值對的個數(shù)System.out.println(maps.size()); // 4// 10. V put(K key,V value) 根據(jù)鍵添加元素值,返回該鍵對應的原值,若沒有原值(添加新元素)則返回nullInteger putValue = maps.put("HTML", 1);Integer putValue2 = maps.put("娃娃", 1);System.out.println(putValue); // nullSystem.out.println(putValue2); // 20System.out.println(maps); // {手表=10, 生活用品=10, HTML=1, 娃娃=1, HUAWEI=100}// 11. void putAll(Map<? extends K, ? extends V> m) 拷貝其他map集合Map<String, Integer> maps1 = new HashMap<>();maps1.put("Java", 1);maps1.put("HTML", 1);Map<String, Integer> maps2 = new HashMap<>();maps2.put("HTML", 2);maps2.put("MySQL", 3);maps1.putAll(maps2); // 鍵"HTML"中的值"2"會覆蓋原值"1"System.out.println(maps1); // {Java=1, MySQL=3, HTML=2}}6.4Map集合的遍歷方式一:鍵找值
Map集合的遍歷方式一:鍵找值
????????先獲取Map集合的全部鍵的Set集合。
????????遍歷鍵的Set集合,然后通過鍵提取對應值。
????????示例代碼如下:
public static void main(String[] args) {Map<String, Integer> maps = new HashMap<>();maps.put("iphoneX", 10);maps.put("娃娃", 20);maps.put("HUAWEI", 100);maps.put("生活用品", 10);maps.put("手表", 10);System.out.println(maps);// 1.拿到集合的全部鍵Set<String> keys = maps.keySet();// 2.遍歷每一個鍵,根據(jù)鍵提取值for (String key : keys) {int value = maps.get(key);System.out.println(key + "-->" + value);}}????????程序運行結(jié)果如下:
{手表=10, 生活用品=10, iphoneX=10, 娃娃=20, HUAWEI=100}
手表-->10
生活用品-->10
iphoneX-->10
娃娃-->20
HUAWEI-->100
6.5Map集合的遍歷方式二:鍵值對
Map集合的遍歷方式二:鍵值對
????????先把Map集合轉(zhuǎn)換成Set集合,Set集合中每個元素都是鍵值對實體類型。
????????遍歷Set集合,然后提取鍵以及提取值。
鍵值對涉及到的API
| 方法名 | 說明 |
| Set<Map.Entry<K,V>> entrySet() | 獲取所有鍵值對對象的集合 |
| K getKey() | 獲得鍵 |
| V getValue() | 獲取值 |
????????示例代碼如下:
public static void main(String[] args) {Map<String, Integer> maps = new HashMap<>();maps.put("iphoneX", 10);maps.put("娃娃", 20);maps.put("iphoneX", 100); // 鍵"iphoneX"中的值"100"會覆蓋之前的值"10"maps.put("HUAWEI", 100);maps.put("生活用品", 10);maps.put("手表", 10);// {手表=10, 生活用品=10, iphoneX=100, 娃娃=20, HUAWEI=100}System.out.println(maps);// 1.將map集合轉(zhuǎn)換為Set集合Set<Map.Entry<String, Integer>> entries = maps.entrySet();for (Map.Entry<String, Integer> entry : entries) {String key = entry.getKey();int value = entry.getValue();System.out.println(key + "-->" + value);}}????????程序運行結(jié)果如下:
{手表=10, 生活用品=10, iphoneX=100, 娃娃=20, HUAWEI=100}
手表-->10
生活用品-->10
iphoneX-->100
娃娃-->20
HUAWEI-->100
6.6Map集合的遍歷方式三:lambda表達式
Map集合的遍歷方式三:Lambda
????????得益于JDK 8開始的新技術Lambda表達式,提供了一種更簡單、更直接的遍歷集合的方式。
Map結(jié)合Lambda遍歷的API
| 方法名 | 說明 |
| default void forEach(BiConsumer<? super K, ? super V> action) | 結(jié)合lambda遍歷Map集合 |
????????示例代碼如下:
public static void main(String[] args) {Map<String, Integer> maps = new HashMap<>();maps.put("iphoneX", 10);maps.put("娃娃", 20);maps.put("iphoneX", 100); // 鍵"iphoneX"中的值"100"會覆蓋之前的值"10"maps.put("HUAWEI", 100);maps.put("生活用品", 10);maps.put("手表", 10);// {手表=10, 生活用品=10, iphoneX=100, 娃娃=20, HUAWEI=100}System.out.println(maps);// maps.forEach(new BiConsumer<String, Integer>() { // @Override // public void accept(String key, Integer value) { // System.out.println(key + "-->" + value); // } // });// lambda表達式簡化匿名內(nèi)部類maps.forEach((key, value) -> System.out.println(key + "-->" + value));}Map集合案例:統(tǒng)計投票人數(shù)
????????需求:某個班級80名學生,現(xiàn)在需要組成秋游活動,班長提供了四個景點依次是(A、B、C、D),每個學生 只能選擇一個景點,請統(tǒng)計出最終哪個景點想去的人數(shù)最多。
分析:
????????將80個學生選擇的數(shù)據(jù)拿到程序中去。
????????定義Map集合用于存儲最終統(tǒng)計的結(jié)果。
????????遍歷80個學生選擇的數(shù)據(jù),看Map集合中是否存在,不存在存入“數(shù)據(jù)=1“,存在則其對應值+1。
????????示例代碼如下:
public static void main(String[] args) {// 1.拿到80個學生的選擇數(shù)據(jù)String[] selects = {"A", "B", "C", "D"};StringBuilder sb = new StringBuilder();Random r = new Random();for (int i = 0; i < 80; i++) {sb.append(selects[r.nextInt(selects.length)]);}System.out.println(sb);// DACCBAACDAACBACDBCCABBAABAAADBDCBBDCCAACCADDAADCAADADACAADCDBCBCCACACBBBCBADDBCA// 2.定義一個map集合記錄最終統(tǒng)計結(jié)果Map<Character, Integer> infos = new HashMap<>();// 3.遍歷學生選擇數(shù)據(jù)for (int i = 0; i < sb.length(); i++) {// 4.提取當前選擇字符char ch = sb.charAt(i);// 5.判斷當前Map集合中是否存在這個鍵if (infos.containsKey(ch)) { // 存在,說明之前出現(xiàn)過該景點選擇// 值+1infos.put(ch, infos.get(ch) + 1);} else { // 不存在,說明該景點選擇是第一次出現(xiàn)// 值=1infos.put(ch, 1);}}System.out.println(infos); // {A=27, B=16, C=22, D=15}}6.7Map集合的實現(xiàn)類HashMap、LinkedHashMap、TreeMap
6.7.1HashMap的特點和底層原理
????????實際上:Set系列集合的底層就是Map實現(xiàn)的,只是Set集合中的元素只要鍵數(shù)據(jù),不要值數(shù)據(jù)而已。
????????由鍵決定:無序、不重復、無索引。HashMap底層是哈希表結(jié)構(gòu)的。
????????依賴hashCode方法和equals方法保證鍵的唯一。
????????如果鍵要存儲的是自定義對象,需要重寫hashCode和equals方法。
????????基于哈希表。增刪改查的性能都較好。
6.7.2LinkedHashMap集合概述和特點
????????由鍵決定:有序、不重復、無索引。
????????這里的有序指的是保證存儲和取出的元素順序一致。
????????原理:底層數(shù)據(jù)結(jié)構(gòu)是依然哈希表,只是每個鍵值對元素又額外的多了一個雙鏈表的機制記錄存儲的順序。
6.7.3TreeMap集合概述和特點
????????由鍵決定特性:不重復、無索引、排序。
????????排序:按照鍵數(shù)據(jù)的大小默認升序(有小到大)排序,只能對鍵排序。
????????注意:TreeMap集合一定要排序,可以默認排序,也可以將鍵按照指定的規(guī)則進行排序。
????????TreeMap和TreeSet底層原理是一樣的。
TreeMap集合自定義排序規(guī)則有2種:
????????類實現(xiàn)Comparable接口,重寫比較規(guī)則。
????????集合自定義Comparator比較器對象,重寫比較規(guī)則。
7.補充知識:集合的嵌套
案例—Map集合案例-統(tǒng)計投票人數(shù)
????????需求:某個班級多名學生,現(xiàn)在需要組成秋游活動,班長提供了四個景點依次是(A、B、C、D),每個學 生可以選擇多個景點,請統(tǒng)計出最終哪個景點想去的人數(shù)最多。
分析:
????????將80個學生選擇的數(shù)據(jù)拿到程序中去,需要記住每個學生選擇的情況。
????????定義Map集合用于存儲最終統(tǒng)計的結(jié)果。
????????示例代碼如下:
public static void main(String[] args) {// 1.拿到80個學生的選擇數(shù)據(jù)// 使用Map集合存儲Map<String, List<String>> data = new HashMap<>();// 2.將學生選擇數(shù)據(jù)存儲進去List<String> selects1 = new ArrayList<>();Collections.addAll(selects1, "A", "C");data.put("張三", selects1);List<String> selects2 = new ArrayList<>();Collections.addAll(selects2, "B", "C", "D");data.put("李四", selects2);List<String> selects3 = new ArrayList<>();Collections.addAll(selects3, "A", "D");data.put("王五", selects3);System.out.println(data); // {李四=[B, C, D], 張三=[A, C], 王五=[A, D]}// 3.統(tǒng)計每個景點的選擇人數(shù)Map<String, Integer> maps = new HashMap<>();// 4.獲取所有人選擇的景點的信息Collection<List<String>> values = data.values();// values = [[B, C, D], [A, C], [A, D]]for (List<String> value : values) { // 遍歷data集合中所有的值,每個值均為一個List集合for (String str : value) { // 遍歷某個List集合中所有的值,每個值均為一個景點選項// 5.判斷當前maps集合中是否存在這個鍵if (maps.containsKey(str)) { // 存在,說明之前出現(xiàn)過該景點選擇// 值+1maps.put(str, maps.get(str) + 1);} else { // 不存在,說明該景點選擇是第一次出現(xiàn)// 值=1maps.put(str, 1);}}}System.out.println(maps); // {A=2, B=1, C=2, D=2}}寫在最后:
? ? ? ? 感謝讀完!
? ? ? ? 縱然緩慢,馳而不息!加油!
總結(jié)
以上是生活随笔為你收集整理的Java SE基础知识详解第[12]期—集合(Set、Collections、Map、集合嵌套)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql获取汉字首字母拼音,包括复杂字
- 下一篇: css view a if属性,uni-