Java SE基础知识详解第[12]期—集合(Set、Collections、Map、集合嵌套)

简介: Java SE基础知识详解第[12]期—集合(Set、Collections、Map、集合嵌套)

集合(Set、Collections、Map、集合嵌套)

1.Set系列集合

1.1Set系列集系概述

Set系列集合特点

无序:存取顺序不一致

不重复:可以去除重复

无索引:没有带索引的方法,所以不能使用普通for循环遍历,也不能通过索引来获取元素。

Set集合实现类特点

HashSet:无序、不重复、无索引。

LinkedHashSet:有序、不重复、无索引(并不是真的无索引,而是对外没有提供索引相关API)

TreeSet:排序(默认自动升序排序)、不重复、无索引。

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

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

HashSet底层原理

HashSet集合底层采取哈希表存储的数据。

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

哈希表的组成

JDK8之前的,底层使用数组+链表组成

JDK8开始后,底层采用数组+链表+红黑树组成。

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

哈希值

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

Object类的API

publicinthashCode():返回对象的哈希值

对象的哈希值特点

同一个对象多次调用hashCode()方法返回的哈希值是相同的

默认情况下,不同对象的哈希值是不同的。

哈希表的详细流程

创建一个默认长度16,默认加载因为0.75的数组,数组名table。

②根据元素的哈希值跟数组的长度求余计算出应存入的位置(哈希算法)。

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

JDK7新元素占老元素位置,指向老元素,JDK8中新元素挂在老元素下面。

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

JDK1.8版本开始HashSet原理解析

底层结构:哈希表(数组、链表、红黑树的结合体)

当挂在元素下面的数据过多时,查询性能降低,从JDK8开始后,当链表长度超过8的时候,自动转换为红黑树,进一步提高了操作数据的性能。

案例:Set集合去重复

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

分析:

①定义学生类,创建HashSet集合对象,创建学生对象。

②把学生添加到集合。

③在学生类中重写两个方法,hashCode()和equals(),自动生成即可。

④遍历集合(增强for)。

示例代码如下:

Student类

publicclassStudent {
privateStringname;
privateintage;
privatecharsex;
publicStudent() {
    }
publicStudent(Stringname, intage, charsex) {
this.name=name;
this.age=age;
this.sex=sex;
    }
publicStringgetName() {
returnname;
    }
publicvoidsetName(Stringname) {
this.name=name;
    }
publicintgetAge() {
returnage;
    }
publicvoidsetAge(intage) {
this.age=age;
    }
publicchargetSex() {
returnsex;
    }
publicvoidsetSex(charsex) {
this.sex=sex;
    }
/*** 两个对象中每个字段内容相同即返回true** @param o* @return*/@Overridepublicbooleanequals(Objecto) {
if (this==o) returntrue;
if (o==nul||getClass() !=o.getClass()) returnfalse;
Studentstudent= (Student) o;
returnage==student.age&&sex==student.sex&&Objects.equals(name, student.name);
    }
@OverridepublicinthashCode() {
returnObjects.hash(name, age, sex);
    }
@OverridepublicStringtoString() {
return"Student{"+"name='"+name+'\''+", age="+age+", sex="+sex+'}';
    }
}

测试类

publicclassSetDemo2 {
publicstaticvoidmain(String[] args) {
/*需求:创建一个存储学生对象的集合,存储多个学生对象,使用程序实现在控制台遍历该集合要求:学生对象的成员变量值相同,我们就认为是同一个对象*/Set<Student>sets=newHashSet<>();
sets.add(newStudent("张三", 18, '男'));
sets.add(newStudent("李四", 20, '男'));
sets.add(newStudent("李四", 20, '男'));
System.out.println(sets); // [Student{name='李四', age=20, sex=男}, Student{name='张三', age=18, sex=男}]// Set集合去重复原理:先判断哈希值,再判断equals// 若没有重写hasCode()与equals()方法,打印结果为[Student{name='李四', age=20, sex=男}, Student{name='张三', age=18, sex=男}, Student{name='李四', age=20, sex=男}]// 没有去重复,两个对象地址不同,哈希值就不同,存储位置也就不同    }
}

自动生成重写hasCode()与equals()方法步骤如下。

①如下图所示,在Student类中空白处点击【鼠标右键】,选择【生成】。

Generate.png

②如下图所示,在弹出的页面中选择【equals()和hasCode()】。

GenerateEqualsAndHasCode.png

③在弹出的页面中依次选择【下一个】,最后选择【完成】,如下图所示,即可自动生成重写的equals()与hasCode()方法。

EqualsAndHasCode.png

1.3实现类:LinkedHashSet

LinkedHashSet集合概述和特点

有序、不重复、无索引。

这里的有序指的是保证存储和取出的元素顺序一致。

原理:底层数据结构依然是哈希表,只是每个元素又额外的多了一个双链表记录存储顺序

1.4实现类:TreeSet

TreeSet集合概述和特点

不重复、无索引、可排序。

可排序:按照元素的大小默认升序(从小到大)排序

TreeSet集合底层是基于红黑树的数据结构实现排序的,增删改查性能都较好。

注意:TreeSet集合是一定要排序的,可以将元素按照指定的规则进行排序。

TreeSet集合默认的规则

对于数值类型:Integer , Double,官方默认按照大小进行升序排序。

对于字符串类型:默认按照首字符的编号升序排序。

对于自定义类型如Student对象,TreeSet无法直接排序。

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

自定义排序规则

TreeSet集合存储对象的的时候有2种方式可以设计自定义比较规则。

方式一:让自定义类(如学生类)实现Comparable接口重写里面的compareTo方法来制定比较规则。

方式二:TreeSet集合有参数构造器,可以设置Comparator接口对应的比较器对象,来制定比较规则。

关于返回值的规则:

如果认为第一个元素大于第二个元素返回正整数即可。

如果认为第一个元素小于第二个元素返回负整数即可。

如果认为第一个元素等于第二个元素返回0即可,此时Treeset集合只会保留一个元素,认为两者重复。

示例代码如下:

Apple类

publicclassAppleimplementsComparable<Apple> {
privateStringname;
privateStringcolor;
privatedoubleprice;
privateintweight;
publicApple() {
    }
publicApple(Stringname, Stringcolor, doubleprice, intweight) {
this.name=name;
this.color=color;
this.price=price;
this.weight=weight;
    }
@OverridepublicStringtoString() {
return"Apple{"+"name='"+name+'\''+", color='"+color+'\''+", price="+price+", weight="+weight+'}';
    }
publicStringgetName() {
returnname;
    }
publicvoidsetName(Stringname) {
this.name=name;
    }
publicStringgetColor() {
returncolor;
    }
publicvoidsetColor(Stringcolor) {
this.color=color;
    }
publicdoublegetPrice() {
returnprice;
    }
publicvoidsetPrice(doubleprice) {
this.price=price;
    }
publicintgetWeight() {
returnweight;
    }
publicvoidsetWeight(intweight) {
this.weight=weight;
    }
/*** 方式一:类自定义比较规则** @param o* @return*/@OverridepublicintcompareTo(Appleo) {
// 按照重量从小到大排序//        return this.weight - o.weight; // "金元帅"与"金冠"的"weight"属性都为500,TreeSet集合在排序时认为二者重复,会删去一个returnthis.weight-o.weight>=0?1 : -1; // 去掉二者相等返回0的情况,保证"weight"相等的数据都会得到保留    }
}

测试类

publicclassSetDemo3 {
publicstaticvoidmain(String[] args) {
// 存储数值类型:Integer为例Set<Integer>sets=newTreeSet<>(); // 不重复 无索引 可排序sets.add(10);
sets.add(23);
sets.add(7);
System.out.println(sets); // [7, 10, 23]// 存储字符串类型:StringSet<String>sets2=newTreeSet<>();
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=newTreeSet<>();
sets3.add(newApple("红富士", "红色", 9.9, 500));
sets3.add(newApple("青苹果", "青色", 12.5, 300));
sets3.add(newApple("金元帅", "金色", 20, 600));
sets3.add(newApple("金冠", "金色", 25.5, 500));
// 如果没有制定排序规则直接打印集合,则无法排序,会报错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>() {// 方式二:集合自带比较器对象进行规则制定// 注:如果TreeSet集合存储的对象有实现比较规则,集合也自带比较器,默认使用集合自带的比较器排序。@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"相等的数据都会得到保留}});*/// lambda表达式简化Set<Apple>apples=newTreeSet<>((o1, o2) ->o1.getWeight() -o2.getWeight() >=0?1 : -1);
apples.add(newApple("红富士", "红色", 9.9, 500));
apples.add(newApple("青苹果", "青色", 12.5, 300));
apples.add(newApple("金元帅", "金色", 20, 600));
apples.add(newApple("金冠", "金色", 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集合存储的对象有实现比较规则,集合也自带比较器,默认使用集合自带的比较器排序

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

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

用ArrayList集合,基于数组的。(用的最多)

2.如果希望元素可以重复,又有索引,增删首尾操作快?

用LinkedList集合,基于链表的。

3.如果希望增删改查都快,但是元素不重复、无序、无索引。

用HashSet集合,基于哈希表的。

4.如果希望增删改查都快,但是元素不重复、有序、无索引。

用LinkedHashSet集合,基于哈希表和双链表。

5.如果要对对象进行排序。

用TreeSet集合,基于红黑树。后续也可以用List集合实现排序。

3.补充知识:可变参数

可变参数概述

可变参数用在形参中可以接收多个数据。

可变参数的格式:数据类型...参数名称

可变参数的作用

传输参数非常灵活,方便。可以不传输参数,可以传输1个或者多个,也可以传输一个数组

可变参数在方法内部本质上就是一个数组。

可变参数的注意事项:

1.一个形参列表中可变参数只能有一个。

2.可变参数必须放在形参列表的最后面。

示例代码如下:

publicclassMethodDemo {
publicstaticvoidmain(String[] args) {
// 1.不传参数sum();
// 2.传输1个参数sum(10);
// 3.传输多个参数sum(10, 20, 50);
// 4.传输数组sum(newint[]{10, 20, 30});
    }
publicstaticvoidsum(int... nums) {
// 可变参数在方法内部实际上就是一个数组System.out.println("元素个数:"+nums.length);
System.out.println("元素内容:"+Arrays.toString(nums));
System.out.println("-----------");
    }
}

程序运行结果如下:

元素个数:0

元素内容:[]

-----------

元素个数:1

元素内容:[10]

-----------

元素个数:3

元素内容:[10, 20, 50]

-----------

元素个数:3

元素内容:[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一键修改所有出现的该参数名称

具有值特性的类型对象排序

方法名

说明

public static <T> void sort(List<T> list)

将集合中(具有值特性的)元素按照默认规则(从小到大)排序

 

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

自定义类型对象排序

方法名

说明

public static <T> void sort(List<T> list,Comparator<? super T> c)

将集合中元素按照指定规则排序

自定义类型实现比较规则Comparable接口

 

 

示例代码如下:

Apple类

publicclassAppleimplementsComparable<Apple> {
privateStringname;
privateStringcolor;
privatedoubleprice;
privateintweight;
publicApple() {
    }
publicApple(Stringname, Stringcolor, doubleprice, intweight) {
this.name=name;
this.color=color;
this.price=price;
this.weight=weight;
    }
@OverridepublicStringtoString() {
return"Apple{"+"name='"+name+'\''+", color='"+color+'\''+", price="+price+", weight="+weight+'}';
    }
publicStringgetName() {
returnname;
    }
publicvoidsetName(Stringname) {
this.name=name;
    }
publicStringgetColor() {
returncolor;
    }
publicvoidsetColor(Stringcolor) {
this.color=color;
    }
publicdoublegetPrice() {
returnprice;
    }
publicvoidsetPrice(doubleprice) {
this.price=price;
    }
publicintgetWeight() {
returnweight;
    }
publicvoidsetWeight(intweight) {
this.weight=weight;
    }
/*** 方式一:类自定义比较规则** @param o* @return*/@OverridepublicintcompareTo(Appleo) {
// 按照重量从小到大排序returnthis.weight-o.weight; // List集合允许重复,不会删去重复元素,即相等也无妨    }
}

测试类

publicclassCollectionsDemo1 {
publicstaticvoidmain(String[] args) {
List<String>names=newArrayList<>();
// 1.   public static <T> boolean addAll(Collection<? super T> c, T... elements) 给集合对象批量添加元素//        names.add("张三");//        names.add("李四");//        names.add("王五");// 添加数据比较麻烦,使用addAll方法批量添加数据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) 将集合中(具有值特性的)元素按照默认规则(从小到大)排序List<Integer>list=newArrayList<>();
Collections.addAll(list, 2, 5, 19, 10, 3);
Collections.sort(list);
System.out.println(list); // [2, 3, 5, 10, 19]// 自定义类型对象排序// ①在自定义类型中实现比较规则Comparable接口List<Apple>apples=newArrayList<>();
apples.add(newApple("红富士", "红色", 9.9, 500));
apples.add(newApple("青苹果", "青色", 12.5, 300));
apples.add(newApple("金元帅", "金色", 20, 600));
apples.add(newApple("金冠", "金色", 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}]// ②通过重写比较器对象中的比较方法,自定义比较规则// 4.   public static <T> void sort(List<T> list,Comparator<? super T> c) 将集合中元素按照指定规则排序//        Collections.sort(apples, new Comparator<Apple>() {//            @Override//            public int compare(Apple o1, Apple o2) {//                // 根据价格升序排序//                return Double.compare(o1.getPrice(), o2.getPrice()); // public static int compare(double d1, double d2)方法比较两个double类型数据,返回1或-1//            }//        });// 使用lambda表达式简化匿名内部类的写法Collections.sort(apples, (o1, o2) ->Double.compare(o1.getPrice(), o2.getPrice())); // public static int compare(double d1, double d2)方法比较两个double类型数据,返回1或-1System.out.println(apples); // 重写比较器对象中的比较方法方式优先级高于自定义类型中实现比较规则Comparable接口,优先匹配重写比较器对象中的比较方法方式// [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}]    }
}

注:重写比较器对象中的比较方法方式优先级高于自定义类型中实现比较规则Comparable接口,优先匹配重写比较器对象中的比较方法方式

5.Collection体系的综合案例

案例—斗地主游戏

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

分析:

①:当系统启动的同时需要准备好数据,可以使用静态代码块来完成。

②:洗牌就是打乱牌的顺序。

③:定义三个玩家、依次发出51张牌。

④:给玩家的牌进行排序(拓展)。

⑤:输出每个玩家的牌数据。

示例代码如下:

Card类

publicclassCard {
privateStringsize; // 点数privateStringcolor; // 花色privateintindex; // 扑克牌权值大小publicCard() {
    }
publicCard(Stringsize, Stringcolor, intindex) {
this.size=size;
this.color=color;
this.index=index;
    }
@OverridepublicStringtoString() {
returncolor+size;
    }
// getter、setter}

测试类

publicclassGameDemo {
// 定义一个静态的集合存储54张牌publicstaticList<Card>allCards=newArrayList<>();
staticintindex; // 记录扑克牌权值大小// 定义静态代码块初始化牌的数据static {
// 初始化数据:点数确定、类型确定,使用数组String[] sizes= {"3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "A", "2"};
// 初始化数据:花色数目确定、类型确定,使用数组String[] colors= {"♥", "♦", "♣", "♠"};
for (Stringcolor : colors) {
index=0; // 表示sizes数组中的"3"权值最小,仅表示大小关系,不一定是0,其他数目也可以for (Stringsize : sizes) {
index++; // 每向后查询一次,扑克牌权值大小+1,代表具有更大的效力// 封装成一个牌对象Cardcard=newCard(size, color, index);
// 将牌对象封装到集合容器中allCards.add(card);
            }
        }
// 此时index代表数字中效力最大的"2"的权值,大王>小王>2// 将大、小王牌对象封装到集合容器中allCards.add(newCard("", "小王", ++index));
allCards.add(newCard("", "大王", ++index));
System.out.println("新牌:"+allCards);
    }
publicstaticvoidmain(String[] args) {
// 洗牌—打乱扑克牌顺序Collections.shuffle(allCards);
System.out.println("洗牌后:"+allCards);
// 定义3个ArrayList集合容器,代表3位玩家手中的扑克牌List<Card>cardList1=newArrayList<>();
List<Card>cardList2=newArrayList<>();
List<Card>cardList3=newArrayList<>();
// 发牌—在牌堆中分别给3位玩家发17张牌,剩余3张作为底牌for (inti=0; i<allCards.size() -3; i++) {
Cardcard=allCards.get(i);
// 轮循:第n张牌发给第1位玩家;第n + 1张牌发给第2位玩家;第n + 3张牌发给第3位玩家// 通过取余的方式实现switch (i%3) {
case0:
cardList1.add(card);
break;
case1:
cardList2.add(card);
break;
case2:
cardList3.add(card);
break;
default:
System.out.println("系统错误!");
            }
        }
// 拿到3张底牌// 通过 List<E> subList(int fromIndex, int toIndex)方法(左闭右开)把最后3张牌截取为子集合List<Card>lastThreeCards=allCards.subList(allCards.size() -3, allCards.size());
// 模拟抢地主Scannersc=newScanner(System.in);
System.out.println("请输入1、2、3第几位玩家是地主:");
intlandlord=sc.nextInt();
// 为地主玩家分配底牌switch (landlord) {
case1:
cardList1.addAll(lastThreeCards);
break;
case2:
cardList2.addAll(lastThreeCards);
break;
case3:
cardList3.addAll(lastThreeCards);
break;
default:
System.out.println("系统错误!");
        }
// 给玩家手牌排序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*/privatestaticvoidsortCards(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集合是一种双列集合每个元素包含两个数据

Map集合的每个元素的格式:key=value(键值对元素)

Map集合也被称为“键值对集合”。

Map集合整体格式:

Collection集合的格式:[元素1,元素2,元素3..]

Map集合的完整格式:{key1=value1 , key2=value2 , key3=value3 , ...}

6.2Map集合体系特点

Map集合体系如下图所示。

MapSystem.png

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

Map集合体系特点

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

Map集合的键是无序,不重复,无索引,值不做要求(可以重复)

Map集合后面重复的键对应的值会覆盖前面重复键的值

Map集合的键和值均可以为null

Map集合实现类特点

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

LinkedHashMap:元素按照键是有序,不重复,无索引,值不做要求。

TreeMap:元素按照建是排序,不重复,无索引的,值不做要求。

示例代码如下:

publicstaticvoidmain(String[] args) {
Map<String, Integer>maps=newHashMap<>();
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)

根据键添加元素值,返回该键对应的原值,

若没有原值(添加新元素)则返回null

V get(Object key)

根据键获取对应值

V remove(Object key)

根据键删除键值对元素,

返回被删除键对应的值

void clear()

清空集合

boolean containsKey(Object key)

判断集合是否包含指定的键

boolean containsValue(Object value)

判断集合是否包含指定的值

boolean isEmpty()

判断集合是否为空

int size()

集合的长度,也就是集合中键值对的个数

Set<K> keySet()

获取全部键的集合

Collection<V> values()

获取全部值的集合,不会删去重复的值

void putAll(Map<? extends K, ? extends V> m)

拷贝其他map集合,后面重复的键对应的值会覆盖前面重复键的值

 

示例代码如下:

publicstaticvoidmain(String[] args) {
Map<String, Integer>maps=newHashMap<>();
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)   根据键获取对应值Integervalue=maps.get("HUAWEI");
System.out.println(value); // 100System.out.println(maps.get("Nike")); // nu 若没有该键,取出的值为"null"// 4.   V remove(Object key) 根据键删除键值对元素,返回被删除键对应的值Integervalue2=maps.remove("iphoneX");
System.out.println(value2); // 100// 5.   boolean containsKey(Object key) 判断集合是否包含指定的键booleanisContainsKey=maps.containsKey("手表");
System.out.println(isContainsKey); // trueSystem.out.println(maps.containsKey("iphoneX")); // false// 6.   boolean containsValue(Object value) 判断集合是否包含指定的值booleanisContainsValue=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() 集合的长度,也就是集合中键值对的个数System.out.println(maps.size()); // 4// 10.  V put(K key,V value) 根据键添加元素值,返回该键对应的原值,若没有原值(添加新元素)则返回nullIntegerputValue=maps.put("HTML", 1);
IntegerputValue2=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=newHashMap<>();
maps1.put("Java", 1);
maps1.put("HTML", 1);
Map<String, Integer>maps2=newHashMap<>();
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集合,然后通过键提取对应值。

示例代码如下:

publicstaticvoidmain(String[] args) {
Map<String, Integer>maps=newHashMap<>();
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.遍历每一个键,根据键提取值for (Stringkey : keys) {
intvalue=maps.get(key);
System.out.println(key+"-->"+value);
        }
    }

程序运行结果如下:

{手表=10, 生活用品=10, iphoneX=10, 娃娃=20, HUAWEI=100}

手表-->10

生活用品-->10

iphoneX-->10

娃娃-->20

HUAWEI-->100

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

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

先把Map集合转换成Set集合,Set集合中每个元素都是键值对实体类型

遍历Set集合,然后提取键以及提取值。

键值对涉及到的API

方法名

说明

Set<Map.Entry<K,V>> entrySet()

获取所有键值对对象的集合

K getKey()

获得键

V getValue()

获取值

 

示例代码如下:

publicstaticvoidmain(String[] args) {
Map<String, Integer>maps=newHashMap<>();
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集合转换为Set集合Set<Map.Entry<String, Integer>>entries=maps.entrySet();
for (Map.Entry<String, Integer>entry : entries) {
Stringkey=entry.getKey();
intvalue=entry.getValue();
System.out.println(key+"-->"+value);
        }
    }

程序运行结果如下:

{手表=10, 生活用品=10, iphoneX=100, 娃娃=20, HUAWEI=100}

手表-->10

生活用品-->10

iphoneX-->100

娃娃-->20

HUAWEI-->100

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

Map集合的遍历方式三:Lambda

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

Map结合Lambda遍历的API

方法名

说明

default void forEach(BiConsumer<? super K, ? super V> action)

结合lambda遍历Map集合

 

示例代码如下:

publicstaticvoidmain(String[] args) {
Map<String, Integer>maps=newHashMap<>();
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表达式简化匿名内部类maps.forEach((key, value) ->System.out.println(key+"-->"+value));
    }

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

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

分析:

将80个学生选择的数据拿到程序中去。

定义Map集合用于存储最终统计的结果。

遍历80个学生选择的数据,看Map集合中是否存在,不存在存入“数据=1“,存在则其对应值+1。

示例代码如下:

publicstaticvoidmain(String[] args) {
// 1.拿到80个学生的选择数据String[] selects= {"A", "B", "C", "D"};
StringBuildersb=newStringBuilder();
Randomr=newRandom();
for (inti=0; i<80; i++) {
sb.append(selects[r.nextInt(selects.length)]);
        }
System.out.println(sb);
// DACCBAACDAACBACDBCCABBAABAAADBDCBBDCCAACCADDAADCAADADACAADCDBCBCCACACBBBCBADDBCA// 2.定义一个map集合记录最终统计结果Map<Character, Integer>infos=newHashMap<>();
// 3.遍历学生选择数据for (inti=0; i<sb.length(); i++) {
// 4.提取当前选择字符charch=sb.charAt(i);
// 5.判断当前Map集合中是否存在这个键if (infos.containsKey(ch)) { // 存在,说明之前出现过该景点选择// 值+1infos.put(ch, infos.get(ch) +1);
            } else { // 不存在,说明该景点选择是第一次出现// 值=1infos.put(ch, 1);
            }
        }
System.out.println(infos); // {A=27, B=16, C=22, D=15}    }

6.7Map集合的实现类HashMap、LinkedHashMap、TreeMap

6.7.1HashMap的特点和底层原理

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

由键决定:无序、不重复、无索引。HashMap底层是哈希表结构的。

依赖hashCode方法和equals方法保证的唯一。

如果存储的是自定义对象,需要重写hashCode和equals方法

基于哈希表。增删改查的性能都较好。

6.7.2LinkedHashMap集合概述和特点

由键决定:有序、不重复、无索引。

这里的有序指的是保证存储和取出的元素顺序一致

原理:底层数据结构是依然哈希表,只是每个键值对元素又额外的多了一个双链表的机制记录存储的顺序。

6.7.3TreeMap集合概述和特点

由键决定特性:不重复、无索引、排序

排序:按照键数据的大小默认升序(有小到大)排序,只能对键排序

注意:TreeMap集合一定要排序,可以默认排序,也可以将键按照指定的规则进行排序。

TreeMap和TreeSet底层原理是一样的。

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

类实现Comparable接口,重写比较规则。

集合自定义Comparator比较器对象,重写比较规则。

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

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

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

分析:

将80个学生选择的数据拿到程序中去,需要记住每个学生选择的情况。

定义Map集合用于存储最终统计的结果。

示例代码如下:

publicstaticvoidmain(String[] args) {
// 1.拿到80个学生的选择数据// 使用Map集合存储Map<String, List<String>>data=newHashMap<>();
// 2.将学生选择数据存储进去List<String>selects1=newArrayList<>();
Collections.addAll(selects1, "A", "C");
data.put("张三", selects1);
List<String>selects2=newArrayList<>();
Collections.addAll(selects2, "B", "C", "D");
data.put("李四", selects2);
List<String>selects3=newArrayList<>();
Collections.addAll(selects3, "A", "D");
data.put("王五", selects3);
System.out.println(data); // {李四=[B, C, D], 张三=[A, C], 王五=[A, D]}// 3.统计每个景点的选择人数Map<String, Integer>maps=newHashMap<>();
// 4.获取所有人选择的景点的信息Collection<List<String>>values=data.values();
// values = [[B, C, D], [A, C], [A, D]]for (List<String>value : values) { // 遍历data集合中所有的值,每个值均为一个List集合for (Stringstr : value) { // 遍历某个List集合中所有的值,每个值均为一个景点选项// 5.判断当前maps集合中是否存在这个键if (maps.containsKey(str)) { // 存在,说明之前出现过该景点选择// 值+1maps.put(str, maps.get(str) +1);
                } else { // 不存在,说明该景点选择是第一次出现// 值=1maps.put(str, 1);
                }
            }
        }
System.out.println(maps); // {A=2, B=1, C=2, D=2}    }
相关文章
|
9天前
|
存储 算法 Java
Java Set深度解析:为何它能成为“无重复”的代名词?
Java的集合框架中,Set接口以其“无重复”特性著称。本文解析了Set的实现原理,包括HashSet和TreeSet的不同数据结构和算法,以及如何通过示例代码实现最佳实践。选择合适的Set实现类和正确实现自定义对象的hashCode()和equals()方法是关键。
21 4
|
9天前
|
Java
那些与Java Set擦肩而过的重复元素,都经历了什么?
在Java的世界里,Set如同一位浪漫而坚定的恋人,只对独一无二的元素情有独钟。重复元素虽屡遭拒绝,但通过反思和成长,最终变得独特,赢得了Set的认可。示例代码展示了这一过程,揭示了成长与独特性的浪漫故事。
16 4
|
9天前
|
Java 开发者
Java Set:当“重复”遇见它,秒变“独宠”!
在Java编程中,Set接口确保集合中的元素不重复,每个元素都是独一无二的“独宠”。本文介绍了Set的两种常见实现:HashSet和TreeSet。HashSet基于哈希表实现,提供高效的添加、删除和查找操作;TreeSet基于红黑树实现,不仅去重还能对元素进行排序。通过示例代码,展示了这两种集合的具体应用,帮助开发者更好地理解和使用Set。
17 4
|
11天前
|
Java
Java 8 引入的 Streams 功能强大,提供了一种简洁高效的处理数据集合的方式
Java 8 引入的 Streams 功能强大,提供了一种简洁高效的处理数据集合的方式。本文介绍了 Streams 的基本概念和使用方法,包括创建 Streams、中间操作和终端操作,并通过多个案例详细解析了过滤、映射、归并、排序、分组和并行处理等操作,帮助读者更好地理解和掌握这一重要特性。
21 2
|
11天前
|
安全 Java
Java多线程集合类
本文介绍了Java中线程安全的问题及解决方案。通过示例代码展示了使用`CopyOnWriteArrayList`、`CopyOnWriteArraySet`和`ConcurrentHashMap`来解决多线程环境下集合操作的线程安全问题。这些类通过不同的机制确保了线程安全,提高了并发性能。
|
15天前
|
存储 Java
判断一个元素是否在 Java 中的 Set 集合中
【10月更文挑战第30天】使用`contains()`方法可以方便快捷地判断一个元素是否在Java中的`Set`集合中,但对于自定义对象,需要注意重写`equals()`方法以确保正确的判断结果,同时根据具体的性能需求选择合适的`Set`实现类。
|
14天前
|
存储 Java 开发者
Java Set:无序之美,不重复之魅!
在Java的集合框架中,Set接口以其“无序之美”和“不重复之魅”受到开发者青睐。Set不包含重复元素,不保证元素顺序,通过元素的hashCode()和equals()方法实现唯一性。示例代码展示了如何使用HashSet添加和遍历元素,体现了Set的高效性和简洁性。
27 4
|
14天前
|
存储 算法 Java
为什么Java Set如此“挑剔”,连重复元素都容不下?
在Java的集合框架中,Set是一个独特的接口,它严格要求元素不重复,适用于需要唯一性约束的场景。Set通过内部数据结构(如哈希表或红黑树)和算法(如哈希值和equals()方法)实现这一特性,自动过滤重复元素,简化处理逻辑。示例代码展示了Set如何自动忽略重复元素。
23 1
|
14天前
|
存储 算法 Java
Java中的Set,你真的了解它的“无重复”奥秘吗?
在Java的广阔天地里,Set以其独特的“无重复”特性,在众多数据结构中脱颖而出。本文将揭秘Set的“无重复”奥秘,带你领略其魅力。Set通过哈希算法和equals()方法协同工作,确保元素不重复。通过一个简单的案例,我们将展示HashSet如何实现这一特性。
25 1
|
15天前
|
存储 Java 开发者
在 Java 中,如何遍历一个 Set 集合?
【10月更文挑战第30天】开发者可以根据具体的需求和代码风格选择合适的遍历方式。增强for循环简洁直观,适用于大多数简单的遍历场景;迭代器则更加灵活,可在遍历过程中进行更多复杂的操作;而Lambda表达式和`forEach`方法则提供了一种更简洁的函数式编程风格的遍历方式。

热门文章

最新文章

下一篇
无影云桌面