七.Set系列集合、Map集合体系

需要学会什么?

  • Set系列集合的特点:Set系列集合的特点和底层原理。
  • 集合工具类Collections:快速的对集合进行元素的添加、排序等操作。
  • 综合案例:把Collection家族的集合应用起来解决一些问题。
  • Map集合体系:Map体系的集合能解决什么问题,各自的特点是什么样的。
  • 集合的嵌套:开发中集合中的元素可能又是一种集合形式,这种方式很常见,需要认识,并学会对其进行处理。

1.Set系列集合

a.Set系列集合概述
























































Collection 接口







List 接口







Set 接口







ArrayList 实现类







LinkedList 实现类







HashSet 实现类







LinkedHashSet 实现类







TreeSet 实现类






Set系列集合特点:

  • 无序:存取顺序不一致。
  • 不重复:可以去去除重复。
  • 无索引:没有带索引的方法,所以不能使用普通for循环遍历,也不能通过索引来获取元素。

Set集合实现类特点:

  • HashSet:无序、不重复、无索引。
  • LinkedHashSet:有序、不重复、无索引。
  • TreeSet:排序、不重复、无索引。

Set集合的功能基本与Collection的API一致。

Test.java

import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;public class Test {public static void main(String[] args) {// Set系列集合的特点:HashSet LinkedHashSet TreeSet// 经典代码 多态写法 无序不重复 无索引Set<String> set = new HashSet<>();set.add("SQL");set.add("SQL");set.add("Java");set.add("Java");set.add("Python");set.add("Python");// [Java, Python, SQL]System.out.println(set);// 有序 不重复 无索引Set<String> set1 = new LinkedHashSet<>();set.add("Java");set.add("Java");set.add("Python");set.add("Python");set.add("SQL");set.add("SQL");// [Java, Python, SQL]System.out.println(set);}
}

总结:

  1. Set系列集合的特点?

    • 无序、不冲股份、无索引。
  2. Set集合的实现类特点?
    • HashSet无序、不重复、无索引。
    • LinkedHashSet有序、不重复、无索引。
    • TreeSet可排序、不重复、无索引。

b.HashSet元素无序的底层原理:哈希表

HashSet底层原理:

  • HashSet集合底层采取哈希表存储的数据。
  • 哈希表是一种对于增删改查数据性能都较好的结构。

哈希表的组成:

  • JDK8之前的,底层使用数组+链表组成。
  • JDK8开始后,底层采用数组+链表+红黑树组成。

在了解哈希表之前需要先理解哈希值的概念。

哈希值:

  • 是JDK根据对象的地址,按照某种规则算出来的int类型的数值

Object类的API:

  • public int hashCode():返回对象的哈希值。

对象的哈希值特点:

  • 同一个对象多次调用hashCode()方法返回的哈希值是相同的。
  • 默认情况下,不同对象的哈希值是不同的。

Test.java

public class Test {public static void main(String[] args) {// 目标:学会获取对象的哈希值String name = "Marx";// 2390778System.out.println(name.hashCode());// 2390778System.out.println(name.hashCode());String name1 = "Linus";// 73425103System.out.println(name1.hashCode());// 73425103System.out.println(name1.hashCode());}
}

HashSet1.7版本原理解析:数组+链表+(结合哈希算法)

  1. 创建一个默认长度16的数组,数组名table。
  2. 根据元素的哈希值跟数组的长度求余计算处应存入的位置(哈希算法)。
  3. 判断当前位置是否为null,如果是null直接存入。
  4. 如果位置不为null,表示有元素,则调用equals方法比较。
  5. 如果一样,则不存,如果不一样,则存入数组。
    • JDK7中新元素占老元素位置,指向老元素。
    • JDK8中新元素挂在老元素下面。

结论:哈希表是一种对于增删改查数据性能都较好的结构。

HashSet1.7版本原理解析:数组-链表-(结合哈希算法)

HashSet1.8版本原理解析:数组+链表+红黑树+(结合哈希算法)

  • 底层结构:哈希表(数组、链表、红黑树的结合体)。
  • 当挂在元素下面的数据过多时,查询性能降低,从JDK8开始后,当链表长度超过8的时候,自动转换为红黑树。

结论:JDK8开始后,哈希表对于红黑树的引入进一步提高了操作数据的性能。

HashSet1.8版本原理解析:数组-链表-红黑树-(结合哈希算法)

总结:

  1. Set集合的底层原理是什么样的?

    • JDK8之前,哈希表:底层使用数组+链表组成。
    • JDK8开始后,哈希表:底层采用数组+链表+红黑树组成。
  2. 哈希表的详细流程?

    • 创建一个默认长度16,默认加载因子0.75的数组(加载因子 = 存储数据的个数 / 表的长度),数组名为table。

    • 根据元素的哈希值跟数组的长度计算出应存入的位置。

    • 判断当前位置是否为null,如果是null直接存入,如果位置不为null,表示有元素,则调用equals方法比较属性值,如果一样,则不存,如果不一样,则存入数组。

    • 当数组存满到16*0.75=12时,就自动扩容,每次扩容原来的两倍。

c.HashSet元素去重复的底层原理

HashSet去重复原理解析:

  1. 创建一个默认长度16的数组,数组名table。
  2. 根据元素的哈希值跟数组的长度求余计算处应存入的位置(哈希算法)。
  3. 判断当前位置是否为null,如果是null直接存入。
  4. 如果位置不为null,表示有元素,则调用equals方法比较。
  5. 如果一样,则不存,如果不一样,则存入数组。

结论:如果希望Set集合认为2个内容一样的对象是重复的,必须重写对象的hashCode()和equals()方法。

案例:Set集合去重复

需求:

  • 创建一个存储学生对象的集合,存储多个学生对象,使用程序实现在控制台遍历该集合。
  • 要求:学生对象的成员变量值相同,我们就认为是同一个对象。

分析:

  • 定义学生类,创建HashSet集合对象,创建学生对象。
  • 把学生添加到集合。
  • 把学生类中重写两个方法,hashCode()和equals(),自动生成即可。
  • 遍历集合(增强for)。

Student.java

import java.util.Objects;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;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", sex=" + sex +'}';}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (!(o instanceof Student student)) return false;return getAge() == student.getAge() && getSex() == student.getSex() && Objects.equals(getName(), student.getName());}@Overridepublic int hashCode() {return Objects.hash(getName(), getAge(), getSex());}
}

Test.java

import java.util.HashSet;
import java.util.Set;public class Test {public static void main(String[] args) {// 创建学生对象哈希集合Set<Student> studentHashSet = new HashSet<>();// 创建学生对象Student student1 = new Student("白子画", 1000, '男');Student student2 = new Student("白子画", 1000, '男');Student student3 = new Student("花千骨", 16, '女');// 学生对象添加到 学生对象哈希集合中studentHashSet.add(student1);studentHashSet.add(student2);studentHashSet.add(student3);// 未重写equals()和hashCode() [Student{name='白子画', age=1000, sex=男}, Student{name='花千骨', age=16, sex=女}, Student{name='白子画', age=1000, sex=男}]// 重写equals()和hashCode() [Student{name='白子画', age=1000, sex=男}, Student{name='花千骨', age=16, sex=女}]System.out.println(studentHashSet);// 遍历学生对象哈希集合for (Student student : studentHashSet) {System.out.println(student);}}
}

总结:

  1. 如果希望Set集合认为2个内容相同的对象是重复的应该怎么办?

    • 重写对象的hashCode和equals方法。

d.实现类:LinkedHashSet

LinkedHashSet集合概述和特点:

  • 有序、不重复、无索引。
  • 这里的有序指的是保证存储和取出的元素顺序一致。
  • 原理:底层数据结构是哈希表,只是每个元素又额外多了一个双链表的机制记录存储的顺序。

Test.java

import java.util.LinkedHashSet;
import java.util.Set;public class Test {public static void main(String[] args) {// 创建LinkedHashSet对象// 有序、不重复、无索引Set<String> linkedHashSet = new LinkedHashSet<>();linkedHashSet.add("MySQL");linkedHashSet.add("MySQL");linkedHashSet.add("Java");linkedHashSet.add("Java");linkedHashSet.add("Python");linkedHashSet.add("Python");// [MySQL, Java, Python]System.out.println(linkedHashSet);}
}

总结:

  1. LinkedHashSet集合的特点和原理是怎么样的?

    • 有序、不重复、无索引。
    • 底层基于哈希表,使用双链表记录添加顺序。

e.实现类:TreeSet

TreeSet集合概述和特点:

  • 不重复、无索引、可排序。
  • 可排序:按照元素的大小默认升序(由小到大)排序。
  • TreeSet集合底层是基于红黑树的数据结构实现排序的,增删改查性能都较好。
  • 注意:TreeSet集合是一定要排序的,可以将元素按照指定的规则进行排序

TreeSet集合默认的规则:

  • 对于数值类型:Integer、Double,官方按照大小进行升序排序。
  • 对于字符串类型:默认按照首字符的编号升序排序。
  • 对于自定义类型如Student对象,TreeSet无法直接排序。

结论:想要使用TreeSet存储自定义类型,需要制定排序规则。

自定义排序规则:

  • TreeSet集合存储对象的时候有2种方法可以设计自定义比较规则。
  • 方式一:让自定义类(如苹果类)实现Comparable接口重写里面的compareTo()方法来定制比较规则。
  • 方式二:TreeSet集合有参构造器可以设置Comparable接口对应的比较器对象,来定制比较规则

两种方式种,关于返回值的规则:

  • 如果认为第一个元素大于第二个元素返回正整数即可。
  • 如果认为第一个元素小于第二个元素返回负整数即可。
  • 如果认为第一个元素等于第二个元素返回0即可,此时TreeSet集合只会保留一个元素,认为两者重复。

注意:如果TreeSet集合存储的对象有实现比较规则,集合也自带比较器,默认使用集合自带的比较器排序。

Apple.java

public class Apple implements Comparable<Apple>{private String name;private String color;private double weight;private int id;public Apple() {}public Apple(String name, String color, double weight, int id) {this.name = name;this.color = color;this.weight = weight;this.id = id;}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 getWeight() {return weight;}public void setWeight(double weight) {this.weight = weight;}public int getId() {return id;}public void setId(int id) {this.id = id;}@Overridepublic String toString() {return "Apple{" +"name='" + name + '\'' +", color='" + color + '\'' +", weight=" + weight +", id=" + id +'}';}/*** 方式一:类自定义比较规则** @param apple 苹果类对象* @return 返回值*/@Overridepublic int compareTo(Apple apple) {// 按照重量进行比较 会去掉重量重复的元素return Double.compare(this.weight, apple.weight);// 一旦有重量相等情况 认为第一个比第二个大 此时不会去掉重量重复的元素// return (int) (this.weight - apple.weight) >= 0 ? 1 : -1;}
}

Test.java

import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;public class Test {public static void main(String[] args) {// 不重复、无索引、可排序// 数值类型Set<Integer> set = new TreeSet<>();set.add(23);set.add(24);set.add(25);set.add(1);set.add(2);// [1, 2, 23, 24, 25]System.out.println(set);// 字符串类型Set<String> set1 = new TreeSet<>();set1.add("Java");set1.add("Python");set1.add("SQL");set1.add("C++");set1.add("asp");set1.add("易语言");// [C++, Java, Python, SQL, asp, 易语言]System.out.println(set1);// 自定义对象Set<Apple> appleSet = new TreeSet<>();// 方式二:集合自带比较器对象进行规则定制
//        Set<Apple> appleSet = new TreeSet<>(new Comparator<Apple>() {
//            @Override
//            public int compare(Apple apple, Apple t1) {
//                return Double.compare(apple.getWeight(), t1.getWeight());
//            }
//        });//        Set<Apple> appleSet = new TreeSet<>((apple, t1) -> Double.compare(apple.getWeight(), t1.getWeight()));appleSet.add(new Apple("红富士", "红色", 9.9, 1));appleSet.add(new Apple("嘎拉", "红色", 6.9, 2));appleSet.add(new Apple("澳洲青苹", "绿色", 5.9, 3));appleSet.add(new Apple("黄元帅", "黄色", 9.9, 4));// [Apple{name='澳洲青苹', color='绿色', weight=5.9, id=3}, Apple{name='嘎拉', color='红色', weight=6.9, id=2}, Apple{name='红富士', color='红色', weight=9.9, id=1}]System.out.println(appleSet);}
}

总结:

  1. TreeSet集合的特点是怎么样的?

    • 可排序、不重复、无索引。
    • 底层基于红黑树实现排序,增删改查性能较好。
  2. TreeSet集合自定义排序规则有几种方式?
    • 类实现Comparable接口,重写比较规则。
    • 集合自定义Comparator比较器对象,重写比较规则。

2.Collection体系特点、使用场景总结

  1. 如果希望元素可以重复、又有索引,索引查询要快?

    • 用ArrayList集合,基于数组。(用的最多)
  2. 如果希望元素可以重复,又有索引,增删首尾操作快?
    • 用LinkedList集合,基于链表的。
  3. 如果希望增删改查都快,但是元素不重复、无序、无索引。
    • 用HashSet集合,基于哈希表。
  4. 如果希望增删改查都快,但是元素不重复、有序、无索引。
    • 用LinkedHashSet集合,基于哈希表和双链表。
  5. 如果要对对象进行排序。
    • 用TreeSet集合,基于红黑树。后续也可以用List集合实现排序。

3.补充知识:可变参数

假如需要定义一个方法求和,该方法可以灵活的完成如下需求:

  • 计算1个数据的和。
  • 计算2个数据的和。
  • 计算3个数据的和。
  • 计算N个数据的和,甚至可以支持不接受参数进行调用。

可变参数:

  • 可变参数用在形参种可以接收多个数据。
  • 可变参数的格式:数据类型…参数名称

可变参数的作用:

  • 传输参数非常灵活、方便。可以不传参数,可以传输1个或多个参数,也可以传输一个数组。
  • 可变参数再方法内部本质上就是一个数组

可变参数的注意事项:

  • 一个形参列表中可变参数只能有一个。
  • 可变参数必须放在形参列表的最后面。

Test.java

import java.util.Arrays;public class Test {public static void main(String[] args) {sum();sum(1);sum(1, 2);sum(new int[]{10, 20, 30});}public static void sum(int...nums) {// 注意:可变参数在方法内部其实就是一个数组System.out.println("元素个数:" + nums.length);System.out.println("元素内容:" + Arrays.toString(nums));}
}

4.补充知识:集合工具类Collections

Collections集合工具类:

  • java.utils.Collections:是集合工具类。
  • 作用:Collections并不属于集合,是用来操作集合的工具类。

Collections常用的API:

方法名称 说明
public static boolean addAll(Collection<? super T> c, T…elements) 给集合对象批量添加元素。
public static void shuffle(List<?> list) 打乱List集合元素的顺序。

Collection排序相关API:

  • 使用范围:只能对于List集合的排序。
方法名称 说明
public static void sort(List list) 将集合中元素按照默认规则排序。

注意:本方式不可以直接对自定义类型的List集合排序,除非自定义类型实现了比较规则Comparable接口。

方法名称 说明
public static void sort(List list, Comparator<? super T > c) 将集合中元素按照指定规则排序。

Apple.java

public class Apple implements Comparable<Apple>{private String name;private String color;private double weight;private int id;public Apple() {}public Apple(String name, String color, double weight, int id) {this.name = name;this.color = color;this.weight = weight;this.id = id;}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 getWeight() {return weight;}public void setWeight(double weight) {this.weight = weight;}public int getId() {return id;}public void setId(int id) {this.id = id;}@Overridepublic String toString() {return "Apple{" +"name='" + name + '\'' +", color='" + color + '\'' +", weight=" + weight +", id=" + id +'}';}/*** 方式一:类自定义比较规则** @param apple 苹果类对象* @return 返回值*/@Overridepublic int compareTo(Apple apple) {// 按照重量进行比较 会去掉重量重复的元素return Double.compare(this.weight, apple.getWeight());// 一旦有重量相等情况 认为第一个比第二个大 此时不会去掉重量重复的元素// return (int) (this.weight - apple.weight) >= 0 ? 1 : -1;}
}

Test.java

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;public class Test {public static void main(String[] args) {/* String 类型 */// 创建集合对象List<String> nameList = new ArrayList<>();//        nameList.add("白子画");
//        nameList.add("花千骨");
//        nameList.add("杀阡陌");
//        nameList.add("轻水");
//        nameList.add("霓漫天");// 集合批量添加数据Collections.addAll(nameList, "白子画", "花千骨", "杀阡陌", "轻水", "霓漫天");// [白子画, 花千骨, 杀阡陌, 轻水, 霓漫天]System.out.println(nameList);// 打乱List集合顺序Collections.shuffle(nameList);// [杀阡陌, 霓漫天, 白子画, 轻水, 花千骨]System.out.println(nameList);/* Integer 类型 */List<Integer> list = new ArrayList<>();// 集合批量添加数据Collections.addAll(list, 7, 5, 10, 6);// [7, 5, 10, 6]System.out.println(list);// List集合排序Collections.sort(list);// [5, 6, 7, 10]System.out.println(list);/* 自定义类型 */List<Apple> appleList = new ArrayList<>();appleList.add(new Apple("红富士", "红色", 9.9, 1));appleList.add(new Apple("嘎拉", "红色", 6.9, 2));appleList.add(new Apple("澳洲青苹", "绿色", 5.9, 3));appleList.add(new Apple("黄元帅", "黄色", 9.9, 4));// 方式一:Apple类已经重写了比较规则 可以进行排序Collections.sort(appleList);// [Apple{name='澳洲青苹', color='绿色', weight=5.9, id=3}, Apple{name='嘎拉', color='红色', weight=6.9, id=2}, Apple{name='红富士', color='红色', weight=9.9, id=1}, Apple{name='黄元帅', color='黄色', weight=9.9, id=4}]System.out.println(appleList);// 方式二Collections.sort(appleList, new Comparator<Apple>() {@Overridepublic int compare(Apple apple, Apple t1) {return Double.compare(apple.getWeight(), t1.getWeight());}});// [Apple{name='澳洲青苹', color='绿色', weight=5.9, id=3}, Apple{name='嘎拉', color='红色', weight=6.9, id=2}, Apple{name='红富士', color='红色', weight=9.9, id=1}, Apple{name='黄元帅', color='黄色', weight=9.9, id=4}]System.out.println(appleList);// 使用lambda简化写法Collections.sort(appleList, (apple, t1) -> Double.compare(apple.getWeight(), t1.getWeight()));System.out.println(appleList);}
}

5.Collection体系的综合案例

Ⅰ.案例:斗地主游戏

需求:

  • 在启动游戏房间的时候,应该提前准备好54张牌,完成洗牌、发牌、牌排序逻辑。

分析:

  • 当系统启动的同时需要准备好数据的时候,就可用用静态代码块。
  • 洗牌就是打乱牌的顺序。
  • 定义三个玩家,依次发出51张牌。
  • 给玩家的牌进行排序(拓展)。
  • 输出每个玩家的牌数据。

Card.java

public class Card {// 牌的花色private String color;// 牌的点数private String size;// 牌的真实大小private int index;public Card() {}public Card(String color, String size, int index) {this.color = color;this.size = size;this.index = index;}public String getColor() {return color;}public void setColor(String color) {this.color = color;}public String getSize() {return size;}public void setSize(String size) {this.size = size;}public int getIndex() {return index;}public void setIndex(int index) {this.index = index;}@Overridepublic String toString() {return color + size;}
}

Landlords.java

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;public class Landlords {/*** 1.创建集合存储54张牌对象*/public static List<Card> cardList = new ArrayList<>();/*** 2.定义静态代码块初始化牌数据*/static {// 定义花色String[] colors = {"黑桃", "红桃", "梅花", "方块"};// 定义点数String[] sizes = {"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2"};// 组合点数和花色int index = 0;for (String size : sizes) {// 相同点数 大小值一样index++;for (String color : colors) {// 创建牌Card card = new Card(color, size, index);// 将牌添加到牌集合对象中cardList.add(card);}}// 大小王存入到牌集合对象中Card jokerGrey = new Card("灰", "Joker", ++index);Card jokerRed = new Card("红", "Joker", ++index);Collections.addAll(cardList, jokerGrey, jokerRed);System.out.println("新牌" + cardList);}public static void main(String[] args) {// 洗牌Collections.shuffle(cardList);System.out.println("洗牌后" + cardList);// 定义三个玩家 每个玩家的牌也是一个集合容器List<Card> ZhangSan = new ArrayList<>();List<Card> Lisi = new ArrayList<>();List<Card> WangWu = new ArrayList<>();// 发牌 从牌集合中发出51张牌给三个玩家 剩余三张作为底牌for (int i = 0; i < cardList.size() - 3; i++) {// 当前牌对象Card card = cardList.get(i);if (i % 3 == 0) {ZhangSan.add(card);} else if (i % 3 == 1) {Lisi.add(card);} else {WangWu.add(card);}}// 底牌List<Card> holeCard = cardList.subList(cardList.size() - 3, cardList.size());// 玩家牌进行排序sortCard(ZhangSan);sortCard(Lisi);sortCard(WangWu);sortCard(holeCard);// 查看发牌结果System.out.println("张三" + ZhangSan);System.out.println("李四" + Lisi);System.out.println("王五" + WangWu);System.out.println("三张底牌" + holeCard);}/*** 对牌集合进行排序** @param cardList: 牌集合*/private static void sortCard(List<Card> cardList) {cardList.sort(new Comparator<Card>() {@Overridepublic int compare(Card card1, Card card2) {return card2.getIndex() - card1.getIndex();}});}
}

6.Map集合体系

a.Map集合的概述

Map集合概述和使用:

  • Map集合是一种双列集合,每个元素包含两个数据。
  • Map集合的每个元素的格式:key=value(键值对元素)。
  • Map集合也被称为“键值对集合”。

Map集合整体格式:

  • Collection集合的格式:[元素1, 元素2, 元素3]。
  • Map集合的完整格式:{key1=value1, key2=value2, key3=value3, …}。

Map集合的使用场景之一:购物车系统:

分析:

  • 购物车提供的四个商品和购买的数量在后台需要容器存储。
  • 每个商品对象都一一对应一个购买数量。
  • 把商品对象看成是Map集合的键,购买数量看成Map集合的值。
{商品1=2, 商品2=3, 商品3=2, 商品4=1}

总结:

  1. Map集合是什么?使用场景是什么样的?

    • Map集合是键值对集合。
    • Map集合非常适合做类似购物车这样的业务场景。

b.Map集合体系特点



















































Map 接口







HashMap 实现类







HashTable 实现类







... 接口







LinkedHashMap 实现类







Properties 实现类







TreeMap 实现类






说明:

  • 使用最多的Map集合是HashMap。
  • 重点掌握HashMap,LinkedHashMap,TreeMap。其他的后续理解。

Map集合体系特点:

  • Map集合的特点都是由键决定的。

  • Map集合的键是无序、不重复的,Map集合后面重复的键对应的值会覆盖前面重复键的值。

  • Map集合的值不做要求、可以重复。

  • Map集合的键值对都可以为null。

Map集合实现类特点:

  • HashMap:元素按照键是无序、不重复、无索引,值不做要求。(与Map体系一致)

    import java.util.HashMap;
    import java.util.Map;/*** 目标:认识Map体系的特点:按照无序、不重复、无索引。*/
    public class Test {public static void main(String[] args) {// 创建一个Map集合对象Map<String, Integer> stringIntegerMap = new HashMap<>();stringIntegerMap.put("固态硬盘", 1);stringIntegerMap.put("HDMI线", 2);stringIntegerMap.put("显示屏", 1);stringIntegerMap.put("显示屏", 2);stringIntegerMap.put("键盘", 1);stringIntegerMap.put(null, null);// {HDMI线=2, null=null, 固态硬盘=1, 键盘=1, 显示屏=2}System.out.println(stringIntegerMap);}
    }
  • LinkedHashMap:元素按照键是有序、不重复、无索引、值不做要求。

    import java.util.LinkedHashMap;
    import java.util.Map;public class Test {public static void main(String[] args) {// 创建一个Map集合对象Map<String, Integer> stringIntegerMap = new LinkedHashMap<>();stringIntegerMap.put("HDMI线", 2);stringIntegerMap.put("显示屏", 1);stringIntegerMap.put("显示屏", 2);stringIntegerMap.put("键盘", 1);stringIntegerMap.put("固态硬盘", 1);stringIntegerMap.put(null, null);// {HDMI线=2, 显示屏=2, 键盘=1, 固态硬盘=1, null=null}System.out.println(stringIntegerMap);}
    }
  • TreeMap:元素按照键排序,不重复、无索引、值不做要求。

c.Map集合常用API

Map集合:

  • Map是双列集合的祖宗接口,它的功能是全部双列集合都可以继承使用的。

Map API如下:

方法名称 说明
V put(K key, V value) 添加元素。
V remove(Object key) 根据键删除键值对元素。
void clear() 移除所有的键值对元素。
boolean containsKey(Object key) 判断集合是否包括指定的键。
boolean containValue(Object value) 判断集合是否包含指定的值。
boolean isEmpty() 判断集合是否为空。
int size() 集合的长度,也就是集合中键值对的个数。

Test.java

import java.util.HashMap;
import java.util.Map;public class Test {public static void main(String[] args) {// 创建一个Map集合对象Map<String, Integer> stringIntegerMap = new HashMap<>();stringIntegerMap.put("固态硬盘", 1);stringIntegerMap.put("HDMI线", 2);stringIntegerMap.put("显示屏", 1);stringIntegerMap.put("显示屏", 2);stringIntegerMap.put("键盘", 1);stringIntegerMap.put(null, null);// {HDMI线=2, null=null, 固态硬盘=1, 键盘=1, 显示屏=2}System.out.println(stringIntegerMap);// 判断集合是否为空 为空返回true 反之返回falseSystem.out.println(stringIntegerMap.isEmpty());// 根据键获取对应值 没有该键返回nullSystem.out.println(stringIntegerMap.get("显示屏"));// 根据键删除整个元素 删除键会返回键的值System.out.println(stringIntegerMap.remove("显示屏"));// 判断是否包含某个键 包含返回true 反之返回falseSystem.out.println(stringIntegerMap.containsKey("键盘"));// 判断是否包含某个值System.out.println(stringIntegerMap.containsValue(1));// 获取全部键的集合System.out.println(stringIntegerMap.keySet());// 获取全部值得集合System.out.println(stringIntegerMap.values());// 集合的大小System.out.println(stringIntegerMap.size());// 合并其他Map集合Map<String, Integer> map = new HashMap<>();map.put("短袖", 1);map.put("短裤", 1);map.put("袜子", 3);map.put("运动鞋", 1);// 集合map的元素拷贝一份到stringIntegerMap中stringIntegerMap.putAll(map);System.out.println(stringIntegerMap);// 清空集合stringIntegerMap.clear();System.out.println(stringIntegerMap);}
}

d.Map集合的遍历方式一:键找值

遍历Map集合方式一:键找值

  • 先获取Map集合的全部键的Set集合。
  • 遍历键的Set集合,然后通过键提取对应值。

键找到值涉及到的API:

方法名称 说明
Set keySet() 获取所有键的集合。
V get(Object key) 根据键获取值。

Test.java

import java.util.HashMap;
import java.util.Map;
import java.util.Set;public class Test {public static void main(String[] args) {// 创建一个Map集合对象Map<String, Integer> stringIntegerMap = new HashMap<>();stringIntegerMap.put("固态硬盘", 1);stringIntegerMap.put("HDMI线", 2);stringIntegerMap.put("显示屏", 1);stringIntegerMap.put("显示屏", 2);stringIntegerMap.put("键盘", 1);// 获取集合所有键Set<String> keySet = stringIntegerMap.keySet();for (String key : keySet) {int value = stringIntegerMap.get(key);System.out.println(key + ":" + value);}}
}

e.Map集合的遍历方式二:键值对

遍历Map集合方式二:键值对。

  • 先把Map集合转换成Set集合,Set集合中每个元素都是键值对实体类型。
  • 遍历Set集合,然后提取键以及提取值。

键值对涉及到的API:

方法名称 说明
Set<Map.Entry<K, V>> entrySet() 获取所有键值对对象的集合。
K getKey() 获得键。
V getValue() 获取值。

Test.java

import java.util.HashMap;
import java.util.Map;
import java.util.Set;public class Test {public static void main(String[] args) {// 创建一个Map集合对象Map<String, Integer> stringIntegerMap = new HashMap<>();stringIntegerMap.put("固态硬盘", 1);stringIntegerMap.put("HDMI线", 2);stringIntegerMap.put("显示屏", 1);stringIntegerMap.put("显示屏", 2);stringIntegerMap.put("键盘", 1);// {HDMI线=2, 固态硬盘=1, 键盘=1, 显示屏=2}System.out.println(stringIntegerMap);/* 使用foreach遍历map集合,发现Map集合的键值对元素直接是没有类型的 所以不可以直接foreach遍历集合* 可以通过调用Map的方法 entrySet把Map集合转换成Set集合形式——Set集合中是Map实体类型-键值对类型* Set<Map.Entry<String, Integer>> entries = [(HDMI线=2), (固态硬盘=1), (键盘=1), (显示屏=2)]*  */// 把Map集合转换成Set集合Set<Map.Entry<String, Integer>> entries = stringIntegerMap.entrySet();// 遍历for (Map.Entry<String, Integer> entry : entries) {System.out.println(entry);System.out.println(entry.getKey() + ":" + entry.getValue());}}
}

f.Map集合的遍历方式三:lambda表达式

Map集合的遍历方式三:Lambda

  • 得益于JDK 8开始的新技术Lambda表达式,提供了一种更简单、更直接的遍历集合的方式。

Map结合Lambda遍历的API

方法名称 说明
default void forEach(BiConsumer<? super k, ? super V> action) 结合lambda遍历Map集合。

Test.java

import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;public class Test {public static void main(String[] args) {// 创建一个Map集合对象Map<String, Integer> stringIntegerMap = new HashMap<>();stringIntegerMap.put("固态硬盘", 1);stringIntegerMap.put("HDMI线", 2);stringIntegerMap.put("显示屏", 1);stringIntegerMap.put("显示屏", 2);stringIntegerMap.put("键盘", 1);// {HDMI线=2, 固态硬盘=1, 键盘=1, 显示屏=2}System.out.println(stringIntegerMap);stringIntegerMap.forEach(new BiConsumer<String, Integer>() {@Overridepublic void accept(String key, Integer value) {System.out.println(key + ":" + value);}});stringIntegerMap.forEach((key, value) -> System.out.println(key + ":" + value));}
}

g.Map集合案例

Map集合案例-统计投票人数

需求:

  • 某个班级80名学生,现在需要组成秋游活动,班长提供了四个景点依次是(A、B、C、D),每个学生只能选择一个景点,请统计出最终哪个景点想去的人数最多。

分析:

  • 将80个学生选择的数据拿到程序中去。
  • 定义Map集合用于存储最终统计的结果。
  • 遍历80个学生选择的数据,看Map集合中是否存在,不存在存入“数据=1”,存在则其对应值+1。

Test.java

import java.util.HashMap;
import java.util.Map;
import java.util.Random;public class Test {public static void main(String[] args) {// 把80个学生选择的数据拿进来String[] selects = {"A", "B", "C", "D"};StringBuilder stringBuilder = new StringBuilder();Random random = new Random();// 生成数据 循环80次for (int i = 0; i < 80; i++) {// 从A B C D中选择其一stringBuilder.append(selects[random.nextInt(selects.length)]);}// 定义一个Map集合记录最终统计的结果: A=30 B=20 C=20 D=10 景点是键 出现次数是值Map<Character, Integer> stringIntegerMap = new HashMap<>();for (int i = 0; i < stringBuilder.length(); i++) {// 提取当前景点字符char ch = stringBuilder.charAt(i);// 判断键是否存在于Map集合中if (stringIntegerMap.containsKey(ch)) {// 如果景点存在 出现次数+1stringIntegerMap.put(ch, stringIntegerMap.get(ch) + 1);} else {// 如果景点初次被选 出现次数初始化为1stringIntegerMap.put(ch, 1);}}// {A=22, B=18, C=26, D=14}System.out.println(stringIntegerMap);}
}

h.Map集合的实现类HashMap

HashMap的特点:

  • HashMap是Map里面的一个实现类。特点都是由键决定的:无序、不重复、无索引。
  • 没有额外需要学习的特有方法,直接使用Map里面的方法就可以了。
  • HashMap跟HashSet底层原理是一摸一样的,都是哈希表结构,只是HashMap的每个元素包含两个值而已。

实际上:Set系列集合的底层就是Map实现的,只是Set集合中的元素只要键数据,不要值数据而已。

总结:

  1. HashMap的特点和底层原理?

    • 由键决定:无序、不重复、无索引。HashMap底层都是哈希表结构。
    • 依赖hashCode()和equals()保证键的唯一。
    • 如果键要存储的是自定义对象,需要重写hashCode和equals方法。
    • 基于哈希表,增删改查的性能都较好。

i.Map集合的实现类LinkedHashMap

LinkedHashMap集合概述和特点:

  • 由键决定:有序、不重复、无索引。
  • 这里的有序指的是保证存储和取出的元素顺序一致。
  • 原理:底层数据结构依然是哈希表,只是每个键值对元素又额外的多了一个双链表的机制,记录存储的顺序。

j.Map集合的实现类TreeMap

TreeMap集合概述和特点:

  • 由键决定特性:不重复、无索引、可排序。
  • 可排序:按照键数据的大小默认升序(由小到大)排序。只能对键排序。
  • 注意:TreeMap集合是一定要排序的,可以默认排序,也可以将键按照指定的规则进行排序。
  • TreeMap跟TreeSet底层原理是一样的。

TreeMap集合自定义排序规则有2种:

  • 类实现Comparable接口,重写比较规则。
  • 集合自定义Comparator比较器对象,重写比较规则。

Apple.java

public class Apple implements Comparable<Apple>{private String name;private String color;private double weight;private int id;public Apple() {}public Apple(String name, String color, double weight, int id) {this.name = name;this.color = color;this.weight = weight;this.id = id;}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 getWeight() {return weight;}public void setWeight(double weight) {this.weight = weight;}public int getId() {return id;}public void setId(int id) {this.id = id;}@Overridepublic String toString() {return "Apple{" +"name='" + name + '\'' +", color='" + color + '\'' +", weight=" + weight +", id=" + id +'}';}/*** 方式一:类自定义比较规则** @param apple 苹果类对象* @return 返回值*/@Overridepublic int compareTo(Apple apple) {// 按照重量进行比较 会去掉重量重复的元素return Double.compare(this.weight, apple.getWeight());// 一旦有重量相等情况 认为第一个比第二个大 此时不会去掉重量重复的元素// return (int) (this.weight - apple.weight) >= 0 ? 1 : -1;}
}

Test.java

import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;public class Test {public static void main(String[] args) {/* TreeMap集合自带排序 可排序 不重复(只要大小规则一样就认为重复) 无索引*/Map<Integer, String> map = new TreeMap<>();map.put(12, "张三");map.put(2, "李四");map.put(3, "王五");// {2=李四, 3=王五, 12=张三}System.out.println(map);// 苹果按照重量排序Map<Apple, String> appleStringMap = new TreeMap<>(new Comparator<Apple>() {@Overridepublic int compare(Apple apple1, Apple apple2) {return Double.compare(apple1.getWeight(), apple2.getWeight());}});appleStringMap.put(new Apple("红富士", "红色", 9.9, 1), "河南");appleStringMap.put(new Apple("嘎拉", "红色", 6.9, 2), "甘肃");appleStringMap.put(new Apple("澳洲青苹", "绿色", 5.9, 3), "新疆");appleStringMap.put(new Apple("黄元帅", "黄色", 9.9, 4), "陕西");// {Apple{name='澳洲青苹', color='绿色', weight=5.9, id=3}=新疆, Apple{name='嘎拉', color='红色', weight=6.9, id=2}=甘肃, Apple{name='红富士', color='红色', weight=9.9, id=1}=陕西}System.out.println(appleStringMap);}
}

k.Map集合总结

Map集合实现类特点:

  • HashMap:元素按照键是无序、不重复、无索引,值不做要求,基于哈希表(与Map体系一致)。
  • LinkedHashMap:元素按照键是有序、不重复、无索引、值不做要求,基于哈希表。
  • TreeMap:元素只能按照键排序、不重复、无索引、值不做要求,可以做排序。

7.补充知识:集合的嵌套

a.案例

Map集合案例-统计投票人数

需求:

  • 某个班级多名学生,现在需要组成秋游活动,班长提供了四个景点依次是A、B、C、D,每个学生可以选择多个景点,请统计出最终哪个景点想去的人数最多。

分析:

  • 将80个学生选择的数据拿到程序中去,需要记住每个学生选择的情况。
  • 定义Map集合用于存储最终统计的结果。

Test.java

import java.util.*;public class Test {public static void main(String[] args) {// 使用Map集合存储记录每个学生选择的情况Map<String, List<String>> data = new HashMap<>();// 学生选择的数据存入List<String> select1 = new ArrayList<>();Collections.addAll(select1, "A", "B", "C");data.put("赵大", select1);List<String> select2 = new ArrayList<>();Collections.addAll(select2, "B", "C");data.put("钱二", select2);List<String> select3 = new ArrayList<>();Collections.addAll(select3, "A", "C");data.put("孙三", select3);// {赵大=[A, B, C], 钱二=[B, C], 孙三=[B, C]}System.out.println(data);// 统计每个景点选择的人数Map<String, Integer> map = new HashMap<>();// 提取所有人选择的景点信息// [[A, B, C], [B, C], [A, C]]Collection<List<String>> valuesList = data.values();for (List<String> values : valuesList) {for (String value : values) {if (map.containsKey(value)) {map.put(value, map.get(value) + 1);} else {map.put(value, 1);}}}// {A=2, B=2, C=3}System.out.println(map);}
}

Java进阶(七)Set系列集合、Map集合体系相关推荐

  1. Java——集合(Map集合的两种迭代)

    一,Map集合的第一种迭代 Map集合的第一种迭代,通过get(key)方法,根据键去获取值 package com.wsq.map;import java.util.HashMap; import ...

  2. java中list,set,map集合的区别,及面试要点

    Map集合:链接: Map集合的五种遍历方式及Treemap方法 Set集合:链接: Java中遍历Set集合的三种方法 TreeSet集合:链接: Java深入了解TreeSet,和迭代器遍历方法 ...

  3. 【Groovy】map 集合 ( map 集合操作符重载 | 使用 << 操作符添加一个元素 | 代码示例 )

    文章目录 一.使用 " << " 操作符添加一个元素 二.代码示例 一.使用 " << " 操作符添加一个元素 对 map 集合 使用 ...

  4. 【Groovy】map 集合 ( map 集合操作符重载 | + 操作符重载 | 代码示例 )

    文章目录 一.map 集合 " + " 操作符重载 二.代码示例 一.map 集合 " + " 操作符重载 对 map 集合使用 " + " ...

  5. 【Groovy】map 集合 ( map 集合遍历 | 使用 map 集合的 find 方法遍历 map 集合 | 代码示例 )

    文章目录 一.使用 map 集合的 find 方法遍历 map 集合 二.代码示例 一.使用 map 集合的 find 方法遍历 map 集合 使用 map 集合的 find 方法遍历 map 集合 ...

  6. 【Groovy】map 集合 ( map 集合遍历 | 使用 map 集合的 each 方法遍历 map 集合 | 代码示例 )

    文章目录 一.使用 map 集合的 each 方法遍历 map 集合 二.代码示例 一.使用 map 集合的 each 方法遍历 map 集合 遍历 map 集合 , 可以调用 map 集合的 eac ...

  7. 常见的数据结构:栈 队列 数组 链表 红黑树——List集合 _ HashSet集合、可变参数 collections集合 Map集合

    2021-06-07复习java 一.常见的数据结构 栈(先进后出) 队列 数组 链表 红黑树 二.List集合_介绍&常用方法 ArrayList集合 Linkedlist集合 三.Hash ...

  8. Java基础进阶集合-map集合

    1.Map和Collection没有继承关系. 2.Map集合以key和value的方式存储数据:键值对 key和value都是引用数据类型. key和value都是存储对象的内存地址. key起到主 ...

  9. Java容器 | 基于源码分析Map集合体系

    一.容器之Map集合 集合体系的源码中,Map中的HashMap的设计堪称最经典,涉及数据结构.编程思想.哈希计算等等,在日常开发中对于一些源码的思想进行参考借鉴还是很有必要的. 基础:元素增查删.容 ...

  10. java EE 2019 11 15关于map集合

    Map集合 //特点 存储的键值对(键映射到值的对象 一个映射不能包含重复的键 //每个键最多只能映射一个值) //Map和Collection的区别 //Collection 只能存单一的元素 单列 ...

最新文章

  1. opencv--车牌识别
  2. 【C/C++】链表的理解与使用
  3. Java构造内部类实例
  4. local lua 多个_Lua 级别 CPU 火焰图介绍
  5. RocketMQ(三)—— 集群模式的说明
  6. 10 个步骤让你成为高效的 Web 开发者
  7. centos7 安装ftp服务
  8. maven配置私服信息
  9. [导入]锐道dorado
  10. 微信缓存深度清理,一招清理20G隐藏的缓存垃圾
  11. App性能优化:内存优化
  12. 关于C语言两个小游戏的提示和源码(猜词游戏与控制移动游戏)
  13. 苹果系统微信实况图照片发送-竞品分析初步思考
  14. Sen2Cor+SNAP处理Sentinel数据
  15. 基于健康档案的区域卫生信息平台-总体架构图
  16. 热门软件看点:花季护航蹿红解读
  17. python项目一:外星人入侵:(一)武装飞船
  18. untiy3D 学习笔记
  19. javafx去掉stage的任务栏图标
  20. 吴恩达机器学习:神经网络学习和作业

热门文章

  1. 蓝桥杯基础练习python
  2. pandas将df赋值到另一个df_Python/Pandas:如果匹配的話,將一個df的值添加到另一個df的行的末尾...
  3. 【C语言】形参实参以及参数传递
  4. Pycharm 注册 Pycharm 破解 Pycharm 注册破解 亲测多法 仅此方有效 有效期至2099年
  5. 小程序高级电商前端第1周走进Web全栈工程师<二>
  6. sqlalchemy下连接MYSQL出现的错误:This session is in ‘prepared‘ state; no further SQL can be emitted ...
  7. 七十七、java版商城之直播短视频带货Spring Cloud+SpringBoot+mybatis+uniapp b2b2c o2o 多商家入驻商城 直播带货商城 电子商务
  8. 男人二十岁后应该学会的习惯
  9. 漏洞篇(SQL注入一)
  10. 启明创投邝子平谈禾赛上市:做硬科技领域长线投资人