Day06、Set系列集合-集合嵌套
Set系列集合
![image-20220501211158106]()
Set系列集合特点
无序:存取顺序不一致
不重复:可以去除重复
无索引:没有带索引的方法,所以不能使用普通for循环遍历,也不能通过索引来获取元素
Set 集合实现类特点
HashSet : 无序、不重复、无索引。
LinkedHashSet: 有序、不重复、无索引。
TreeSet: 排序、不重复、无索引。
Set 集合的功能上基本上与collection的API一致。
HashSet底层原理
. HashSet 集合底层采取哈希表存储的数据。
. 哈希表是一种对于增删改查数据性能都较好的结构。
哈希表的组成
. JDK8 之前的, 底层使用数组+ 链表组成
. JDK8 开始后,底层采用数组+ 链表+ 红黑树组成。
哈希值
是JDK根据对象的地址,按照某种规则算出来的int类型的数值
Object类的API
public int hashCode():返回对象的哈希值
对象的哈希值特点
同一个对象多次调用hashCode()方法返回的哈希值是相同的
默认情况下,不同对象的哈希值是不同的。
![image-20220501214052995]()
![image-20220501214244255]()
哈希表的详细流程:
-
创建一个默认长度16 , 默认加载因为0.75 的数组, 数组名table
-
根据元素的哈希值跟数组的长度计算出应存入的位置
-
判断当前位置是否为null , 如果是null 直接存入, 如果位置不为null , 表示有元素,则调用equals 方法比较属性值, 如果一样, 则不存, 如果不一样, 则存入数组。
-
当数组存满到16 * 0.75 = 12 时, 就自动扩容, 每次扩容原先的两倍
HashSet去重复原理解析
![image-20220502120556330]()
案例:
![image-20220502121837239]()
LinkedHashSet集合概述和特点:
有序、不重复、无索引
![image-20220502122049377]()
TreeSet集合概述和特点
-
不重复、无索引、可排序
-
可排序: 按照元素的大小默认升序( 有小到大) 排序。
-
TreeSet 集合底层是基于红黑树的数据结构实现排序的, 增删改查性能都较好。
-
注意: TreeSet 集合是一定要排序的可以将元素按照指定的规则进行排序。
TreeSet集合默认的规则
-
对于数值类型: lnteger, Double, 官方默认按照大小进行升序排序。
-
对于字符串类型: 默认按照首字符的编号升序排序。
-
对于自定义类型如Student 对象, TreeSet 无法直接排序。
结论: 想要使用TreeSet 存储自定义类型需要制定排序规则
自定义排序规则
-
TreeSet 集合存储对象的的时候有2 种方式可以设计自定义比较规则
方式一
-
让自定义的类( 如学生类) 实现Comparable接口重写里面的compareTo方法来定制比较规则。
方式二
-
Treeset 集合有参数构造器, 可以设置Comparator 接口对应的比较器对象, 来定制比较规则。
![image-20220502124417757]()
![image-20220502124921748]()
![image-20220502124939241]()
可变参数
可变参数的作用
-
传输参数非常灵活, 方便。可以不传输参数, 可以传输1 个或者多个, 也可以传输一个数组
-
可变参数在方法内部本质上就是一个数组。
可变参数的注意事项:
-
一个形参列表中可变参数只能有一个
-
可变参数必须放在形参列表的最后面
-
public static void sum1(int age,int... nums)
Collection集合工具类
. java.utiIs.coIIections: 是集合工具类
. 作用: collection并不属于集合, 是用来操作集合的工具类。
Collections 常用的API
| 方法名称 |
说明 |
| public static boolean addAII(CoIIection<? super T> c ,T...elements) |
给集合对象批量添加元素 |
| public static void shuffIe(List<?> list) |
打乱List 集合元素的顺序 |
Collections排序相关API
排序方式1 :
| 方法名称 |
说明 |
| public static void sort(List list) |
将集合中元素按照指定规则排序 |
| 注意: 本方式不可以直接对自定义类型的List 集合排序, 除非自定义类型实现了比较规则Comparable接口。 |
|
排序方式2 :
| 方法名称 |
说明 |
| public static void sort(List 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());
}
});
Collection体系的综合案例:斗地主
public class GameDemo {
/*
1、定义一个静态的集合存储54张牌对象;
*/
public static List<Card> allCards = new ArrayList<>();
/*
2、做牌:定义静态代码块初始化牌数据
*/
static {
//3、定义点数:个数确定,类型确定,使用数组
String[] sizes = {"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2"};
//4.定义花色:个数确定、类型确定,使用数组
String[] colors = {"♠", "♥", "♦", "♣"};
//5.组合点数和花色
int index = 0;
for (String size : sizes) {
index++;
for (String color : colors) {
//6.封装成一个牌对象
Card c = new Card(size, color, index);
//7.存入到集合容器中去
allCards.add(c);
}
}
//8、大小王存入到集合对象中去
Card c1 = new Card("", "🃏", ++index);
Card c2 = new Card("", "🤡", ++index);
Collections.addAll(allCards, c1, c2);
System.out.println("新牌" + allCards);
}
public static void main(String[] args) {
//9、洗牌
Collections.shuffle(allCards);
System.out.println("洗牌后:" + allCards);
//10、发牌(定义三个玩家,每个玩家的牌也是一个集合容器)
List<Card> cafune = new ArrayList<>();
List<Card> dd = new ArrayList<>();
List<Card> dzh = new ArrayList<>();
//11.开始发牌(从牌集合中发出51张牌给3个玩家,三张底牌
//allCards = [K♦, 10♥, K♣, K♠, 6♥, Q♠, J♥, 10♠, 6♣, 4♠, 7♥, 8♦, A♣, 3♥, Q♥];
// i = 0 1 2
for (int i = 0; i < allCards.size() - 3; i++) {
Card c = allCards.get(i);
if (i % 3 == 0) {
cafune.add(c);
} else if (i % 3 == 1) {
dd.add(c);
} else if (i % 3 == 2) {
dzh.add(c);
}
}
//12.拿到最后三张底牌(把最后三张牌截取成一个子集合
List<Card> lastThreeCards = allCards.subList(allCards.size() - 3, allCards.size());
//13.给玩家的牌排序
sortCards(cafune);
sortCards(dd);
sortCards(dzh);
//14.输出玩家的牌
System.out.println("cafune:" + cafune);
System.out.println("dd:" + dd);
System.out.println("dzh:" + dzh);
System.out.println("三张底牌" + lastThreeCards);
}
/*
给牌排序的方法
*/
public static void sortCards(List<Card> cards) {
//cards = [7♣, 2♣, 10♥];
Collections.sort(cards,(o1, o2) -> o1.getIndex() - o2.getIndex());
}
}
public class Card {
private String size;
private String color;
private int index;//牌的真正大小
public Card() {
}
public Card(String size, String color, int index) {
this.size = size;
this.color = color;
this.index = index;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
public String getSize() {
return size;
}
public void setSize(String size) {
this.size = size;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public String toString() {
return size + color;
}
}
Map集合体系
Map 集合概述和使用
-
Map 集合是一种双列集合, 每个元素包含两个数据。
-
Map 集合的每个元素的格式: key=value( 键值对元素)。
-
Map 集合也被称为“ 键值对集合“ 。
-
Map集合适合做类购物车这样的业务场景
Map 集合整体格式:
-
collection集合的格式: [ 元素1 , 元素2 , 元素3 · · ]
-
Map 集合的完整格式:
![image-20220503145310525]()
Map集合体系
![image-20220503145710665]()
![image-20220503145747173]()
Map 集合体系特点
-
Map 集合的特点都是由键决定的。
-
Map 集合的键是无序, 不重复的, 无索引的, 值不做要求( 可以重复) 。
-
Map 集合后面重复的键对应的值会覆盖前面重复键的值。
-
Map 集合的键值对都可以为null 。
Map 集合实现类特点(map.put())
-
HashMap : 元素按照键是无序, 不重复, 无索引, 值不做要求。( 与Map 体系一致)
-
LinkedHashMap:元素按照键是有序, 不重复, 无索引, 值不做要求。
-
TreeMap:元素按照键是排序, 不重复, 无索引, 值不做要求。
Map集合
Map 是双列集合的祖宗接口, 它的功能是全部双列集合都可以继承使用的。
Map API如下:
| 方法名称 |
说明 |
| V put(K key,V value) |
添加元素 |
| V remove (Object key) |
根据键删除键值对元素 |
| void clear() |
移除所有的键值对元素 |
| boolean containsKey(Object key) |
判断集合是否包含指定的键 |
| boolean containsVa1ue(Object value) |
判断集合是否包含指定的值 |
| boolean isEmpty() |
判断集合是否为空 |
| int size() |
集合的长度, 也就是集合中键值对的个数 |
遍历Map集合方式之一:键找值流程
Map 集合的遍历方式一: 键找值
. 先获取Map 集合的全部键的Set 集合。
. 遍历键的Set 集合, 然后通过键提取对应值。
键找值涉及到的API :
| 方法名称 |
说明 |
| Set keySet() |
获取所有键的集合 |
| V get(Object key) |
根据键获取值 |
public class MapDemo01 {
public static void main(String[] args) {
Map<String, Integer> maps = new HashMap<>();
maps.put("iphonex",10);
maps.put("phone",31);
maps.put("iphonex",100);//Map集合后面重复的键对应的元素会覆盖前面重复的整个元素
maps.put("Huawei",1000);
maps.put("生活用品",10);
maps.put("手表",10);
System.out.println(maps);
//1.键找值,第一步,先拿到集合的全部键
Set<String> keys = maps.keySet();
for (String key : keys) {
int value = maps.get(key);
System.out.println(key + "➡️" + value);
}
}
}
Map 集合的遍历方式二: 键值对
先把Map 集合转换成Set 集合, Set 集合中每个元素都是键值对实体类型了。
遍历Set 集合, 然后提取键以及提取值。
键值对涉及到的API:
| 方法名称 |
说明 |
| Set<Map.Entry<K,V>> entrySet() |
获取所有键值对对象的集合 |
| K getKey() |
获得键 |
| V getValue() |
获取值 |
public class MapDemo02 {
public static void main(String[] args) {
Map<String, Integer> maps = new HashMap<>();
maps.put("iphonex",10);
maps.put("phone",31);
maps.put("iphonex",100);//Map集合后面重复的键对应的元素会覆盖前面重复的整个元素
maps.put("Huawei",1000);
maps.put("生活用品",10);
maps.put("手表",10);
System.out.println(maps);
/*
使用foreach遍历map集合,发现Map集合的键值对元素直接是没有类型的,不可以直接foreach遍历集合
可以通过Map方法 entrySet把Map集合转换成set集合形式 maps.entrySet();
Set<Map.Entry<String, Integer>> entries = maps.entrySet();
[(手表=10), (生活用品=10),(phone=31),(iphonex=100),(Huawei=1000)]
entry
*/
//1.把Map集合转换成set集合 使用快捷键补全 ctrl + alt + v
Set<Map.Entry<String, Integer>> entries = maps.entrySet();
//2.开始遍历
for (Map.Entry<String, Integer> entry : entries) {
String key = entry.getKey();
int value = entry.getValue();
System.out.println(key + "➡️" + value);
}
}
}
Map 集合的遍历方式三: Lambda
![image-20220503212453191]()
public class MapDemo03 {
public static void main(String[] args) {
Map<String, Integer> maps = new HashMap<>();
maps.put("iphonex", 10);
maps.put("phone", 31);
maps.put("iphonex", 100);//Map集合后面重复的键对应的元素会覆盖前面重复的整个元素
maps.put("Huawei", 1000);
maps.put("生活用品", 10);
maps.put("手表", 10);
System.out.println(maps);
maps.forEach(new BiConsumer<String, Integer>() {
@Override
public void accept(String key, Integer value) {
System.out.println(key + "➡️" + value);
}
});
}
}
![image-20220503212700135]()
HashMap 的特点
-
HashMap 是Map 里面的一个实现类。特点都是由键决定的: 无序、不重复、无索引
-
没有额外需要学习的特有方法, 直接使用Map 里面的方法就可以了。
-
HashMap 跟层原理是一模一样的, 都是哈希表结构, 只是HashMap 的每个元素包含两个值而已。
-
依赖hashCode方法和equals方法保证键的唯一
-
如果键要存储的是自定义对象,要重写hashcode和equals方法
-
基于哈希表,增删改查性能都比较好
实际上: set 系列集合的底层就是藺ap 实现的, 只是set 集合中的元素只要键数据, 不要值数据而已。
public HashSet() {
map = new HashMap<>();
}
/*
public class HashMapDemo01 {
public static void main(String[] args) {
//Map集合是根据键去除重复元素
HashMap<Student, String> maps = new HashMap<>();
Student s1 = new Student("无恙", 22, '男');
Student s2 = new Student("无恙", 22, '男');
Student s3 = new Student("周雄", 20, '男');
maps.put(s1,"北京");
maps.put(s2,"上海");
maps.put(s3,"广州");
System.out.println(maps);//由于方法重写,所以s1和s2相等,S1会作为键会被S2覆盖
}
}
*/
{Student{name='无恙', age=22, sex=男}=上海, Student{name='周雄', age=20, sex=男}=广州}
LinkedHashMap 集合概述和特点
-
由键决定: 有序、不重复、无索引。
-
这里的有序指的是保证存储和取出的元素顺序一致
-
原理: 底层数据结构是依然哈希表, 只是每个键值对元素又额外的多了一个双链表的机制记录存储的顺序。
![image-20220505191226343]()
TreeMap 集合概述和特点
-
由键决定特性: 不重复、无索引、可排序
-
可排序: 按照键数据的大小默认升序( 有小到大) 排序。只能对键排序。
-
注意: TreeMap 集合是一定要排序的, 可以默认排序, 也可以将键按照指定的规则进行排序
-
TreeMap 跟TreeSet— 样底层原理是一样的。
TreeMap 集合自定义排序规则有两种
-
类实现Comparable接口,重写比较规则
-
集合自定义Comparator比较器对象,重写比较规则
Map 集合实现类特点
-
HashMap : 元素按照键是无序, 不重复, 无索引, 值不做要求, 基于哈希表( 与Map 体系一致)
-
LinkedHashMap: 元素按照键是有序, 不重复, 无索引, 值不做要求, 基于哈希表
-
TreeMap: 元素只能按照键排序, 不重复, 无索引的, 值不做要求, 可以做排序
集合嵌套
案例 统计投票人数
![image-20220505193301034]()
public class MapTest04 {
public static void main(String[] args) {
//1.使用一个Map集合存储
HashMap<String, List<String>> data = new HashMap<>();
//2.把学生选择的数据存入进去
List<String> selects = new ArrayList<>();
Collections.addAll(selects, "A", "C");
data.put("cc", selects);
List<String> selects1 = new ArrayList<>();
Collections.addAll(selects1, "A", "C", "D");
data.put("dd", selects1);
List<String> selects2 = new ArrayList<>();
Collections.addAll(selects2, "A", "B", "C");
data.put("ee", selects2);
System.out.println(data);
//3.统计每个景点选择的人数
HashMap<String, Integer> infos = new HashMap<>();
//4.提取所有人选择的景点的信息
Collection<List<String>> values = data.values();
//values = [[A, C], [A, C, D], [A, B, C]]
// value
System.out.println(values);
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);
}
}