Java - day15 - 集合(Set、Collections、Map、集合嵌套)
Set系列集合
Set系列集系概述
Set系列集合特点
- 无序:存取顺序不一致
- 不重复:可以去除重复
- 无索引:没有带索引的方法,所以不能使用普通for循环遍历,也不能通过索引来获取元素。
Set集合实现类特点
- HashSet:无序、不重复、无索引。
- LinkedHashSet:有序、不重复、无索引。
- TreeSet:排序、不重复、无索引。
Set集合的功能上基本上与Collection的API一致。
HashSet元素无序的底层原理:哈希表
HashSet底层原理
- HashSet集合底层采取哈希表存储的数据。
- 哈希表是一种对于增删改查数据性能都较好的结构。
哈希表的组成
- JDK8之前的,底层使用数组+链表组成
- JDK8开始后,底层采用数组+链表+红黑树组成。
在了解哈希表之前需要先理解哈希值的概念
哈希值
- 是JDK根据对象的地址,按照某种规则算出来的int类型的数值。
Object类的API
public int hashCode()
:返回对象的哈希值
对象的哈希值特点
- 同一个对象多次调用
hashCode()
方法返回的哈希值是相同的 - 默认情况下,不同对象的哈希值是不同的。
哈希表的详细流程
JDK1.8版本开始HashSet原理解析
- 底层结构:哈希表(数组、链表、红黑树的结合体)
- 当挂在元素下面的数据过多时,查询性能降低,从JDK8开始后,当链表长度超过8的时候,自动转换为红黑树。
HashSet元素去重复的底层原理
如果希望Set集合认为2个内容相同的对象是重复的应该怎么办?
- 重写对象的
hashcode和equals方法
。
案例:
package com.itheima.d1_collection_set;
import java.util.HashSet;
import java.util.Set;
/**
目标:让Set集合把重复内容的对象去掉一个(去重复)
*/
public class SetDemo3 {
public static void main(String[] args) {
// Set集合去重复原因:先判断哈希值算出来的存储位置是否相同 再判断equals
Set<Student> sets = new HashSet<>();
Student s1 = new Student("无恙", 20, '男');
Student s2 = new Student("无恙", 20, '男');
Student s3 = new Student("周雄", 21, '男');
System.out.println(s1.hashCode());
System.out.println(s2.hashCode());
System.out.println(s3.hashCode());
sets.add(s1);
sets.add(s2);
sets.add(s3);
System.out.println(sets);
}
}
package com.itheima.d1_collection_set;
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;
}
/**
只要2个对象内容一样,结果一定是true
* @param o
* @return
*/
@Override
public 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 && sex == student.sex && Objects.equals(name, student.name);
}
/**
s1 = new Student("无恙", 20, '男')
s2 = new Student("无恙", 20, '男')
s3 = new Student("周雄", 21, '男')
*/
@Override
public int hashCode() {
return Objects.hash(name, age, sex);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", sex=" + sex +
'}';
}
}
实现类:LinkedHashSet
LinkedHashSet集合概述和特点
- 有序、不重复、无索引。
- 这里的有序指的是保证存储和取出的元素顺序一致
- 原理:底层数据结构是依然哈希表,只是每个元素又额外的多了一个双链表的机制记录存储的顺序。
实现类:TreeSet
TreeSet集合概述和特点
- 不重复、无索引、可排序
- 可排序:按照元素的大小默认升序(有小到大)排序。
- TreeSet集合底层是基于红黑树的数据结构实现排序的,增删改查性能都较好。
- 注意:TreeSet集合是一定要排序的,可以将元素按照指定的规则进行排序。
TreeSet集合默认的规则
- 对于数值类型:Integer , Double,官方默认按照大小进行升序排序。
- 对于字符串类型:默认按照首字符的编号升序排序。
- 对于自定义类型如Student对象,TreeSet无法直接排序。
- 结论:想要使用TreeSet存储自定义类型,需要制定排序规则。
自定义排序规则
- TreeSet集合存储对象的的时候有2种方式可以设计自定义比较规
方式一
- 让自定义的类(如学生类)实现Comparable接口重写里面的compareTo方法来定制比较规则。
方式二
- TreeSet集合有参数构造器,可以设置comparator接口对应的比较器对象,来定制比较规则。
两种方式中,关于返回值的规则:
- 如果认为第一个元素大于第二个元素返回正整数即可。
- 如果认为第一个元素小于第二个元素返回负整数即可。
- 如果认为第一个元素等于第二个元素返回0即可,此时Treeset集合只会保留一个元素,认为两者重复。
Collection体系的特点、使用场景总结
1. 如果希望元素可以重复,又有索引,索引查询要快?
- 用
ArrayList集合
,基于数组的。(用的最多)
2. 如果希望元素可以重复,又有索引,增删首尾操作快?
- 用
LinkedList集合
,基于链表的。
3. 如果希望增删改查都快,但是元素不重复、无序、无索引。
- 用
HashSet集合
,基于哈希表的。
4. 如果希望增删改查都快,但是元素不重复、有序、无索引。
- 用
LinkedHashSet集合
,基于哈希表和双链表。
5. 如果要对对象进行排序。
- 用
Treeset集合
,基于红黑树。后续也可以用List集合实现排序。
补充知识:可变参数
可变参数
- 可变参数用在形参中可以接收多个数据。
- 可变参数的格式:数据类型...参数名称
可变参数的作用
- 传输参数非常灵活,方便。可以不传输参数,可以传输1个或者多个,也可以传输一个数组。
- 可变参数在方法内部本质上就是一个数组。
可变参数的注意事项:
- 一个形参列表中可变参数只能有一个
- 可变参数必须放在形参列表的最后面
补充知识:集合工具类Collections
Collections集合工具类
Collections排序相关API
Collection体系的综合案例
案例:斗地主
Map集合体系
Map集合的概述
Map集合概述和使用
- Map集合是一种双列集合,每个元素包含两个数据。
- Map集合的每个元素的格式: key=value(键值对元素)。
- Map集合也被称为“键值对集合”。
Map集合整体格式:
Map集合体系特点
Map集合体系特点
- Map集合的特点都是由键决定的。
- Map集合的键是无序,不重复的,无索引的,值不做要求(可以重复)。
- Map集合后面重复的键对应的值会覆盖前面重复键的值。
- Map集合的键值对都可以为null。
Map集合实现类特点
HashMap
:元素按照键是无序,不重复,无索引,值不做要求。(与Map体系一致)LinkedHashMap
:元素按照键是有序,不重复,无索引,值不做要求。TreeMap
:元素按照建是排序,不重复,无索引的,值不做要求。
Map集合常用API
Map集合
Map集合的遍历方式一:键找值
Map集合的遍历方式二:键值对 (麻烦一点)
Map集合的遍历方式三:lambda表达式(简单点)
案例
package map;
import java.util.*;
/**
需求:统计投票人数
*/
public class MapTest1 {
public static void main(String[] args) {
// 1、把80个学生选择的数据拿进来。
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);
// 2、定义一个Map集合记录最终统计的结果: A=30 B=20 C=20 D=10 键是景点 值是选择的数量
Map<Character, Integer> infos = new HashMap<>(); //
// 3、遍历80个学生选择的数据
for (int i = 0; i < sb.length(); i++) {
// 4、提取当前选择景点字符
char ch = sb.charAt(i);
// 5、判断Map集合中是否存在这个键
if(infos.containsKey(ch)){
// 让其值 + 1
infos.put(ch , infos.get(ch) + 1);
}else {
// 说明此景点是第一次被选
infos.put(ch , 1);
}
}
// 4、输出集合
System.out.println(infos);
}
}
Map集合的实现类HashMap
HashMap的特点
- HashMap是Map里面的一个实现类。
- 特点都是由键决定的:无序、不重复、无索引。
- 没有额外需要学习的特有方法,直接使用Map里面的方法就可以了。
- HashMap跟HashSet底层原理是一模一样的,都是哈希表结构,只是HashMap的每个元素包含两个值而已。
- 实际上:Set系列集合的底层就是Map实现的,只是Set集合中的元素只要键数据,不要值数据而已。
HashMap的特点和底层原理
- 由键决定:无序、不重复、无索引。
- HashMap底层是哈希表结构的。
- 依赖hashcode方法和equals方法保证键的唯一。
- 如果键要存储的是自定义对象,需要重写hashCode和equals方法。
- 基于哈希表,增删改查的性能都较好。
Map集合的实现类LinkedHashMap
LinkedHashMap集合概述和特点
- 由键决定:有序、不重复、无索引。
- 这里的有序指的是保证存储和取出的元素顺序一致
- 原理:底层数据结构是依然哈希表,只是每个键值对元素又额外的多了一个双链表的机制记录存储的顺序。
Map集合的实现类TreeMap
TreeMap集合概述和特点
- 由键决定特性:不重复、无索引、可排序
- 可排序:按照键数据的大小默认升序(有小到大)排序。只能对键排序。
- 注意:TreeMap集合是一定要排序的,可以默认排序,也可以将键按照指定的规则进行排序
- TreeMap跟TreeSet 一样,底层原理是一样的。
TreeMap集合自定义排序规则有2种
- 类实现Comparable接口,重写比较规则。
- 集合自定义Comparator比较器对象,重写比较规则。
(总结)Map集合实现类特点
HashMap
:元素按照键是无序,不重复,无索引,值不做要求,基于哈希表(与Map体系一致)。LinkedHashMap
:元素按照键是有序,不重复,无索引,值不做要求,基于哈希表。TreeMap
:元素只能按照键排序,不重复,无索引的,值不做要求,可以做排序。
补充知识:集合的嵌套
案例
package com.itheima.d9_map_impl;
import java.util.*;
/**
需求:统计投票人数
*/
public class MapTest4 {
public static void main(String[] args) {
// 1、要求程序记录每个学生选择的情况。
// 使用一个Map集合存储。
Map<String, List<String>> data = new HashMap<>();
// 2、把学生选择的数据存入进去。
List<String> selects = new ArrayList<>();
Collections.addAll(selects, "A", "C");
data.put("罗勇", selects);
List<String> selects1 = new ArrayList<>();
Collections.addAll(selects1, "B", "C" , "D");
data.put("胡涛", selects1);
List<String> selects2 = new ArrayList<>();
Collections.addAll(selects2 , "A", "B", "C" , "D");
data.put("刘军", selects2);
System.out.println(data);
// 3、统计每个景点选择的人数。
Map<String, Integer> infos = new HashMap<>(); // {}
// 4、提取所有人选择的景点的信息。
Collection<List<String>> values = data.values();
System.out.println(values);
// values = [[A, B, C, D], [B, C, D], [A, C]]
// value
for (List<String> value : values) {
for (String s : value) {
// 有没有包含这个景点
if(infos.containsKey(s)){
infos.put(s, infos.get(s) + 1);
}else {
infos.put(s , 1);
}
}
}
System.out.println(infos);
}
}
Comments | NOTHING