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去重复原理解析

案例:

LinkedHashSet集合概述和特点:
有序、不重复、无索引

TreeSet集合概述和特点
-
不重复、无索引、可排序
-
可排序: 按照元素的大小默认升序( 有小到大) 排序。
-
TreeSet 集合底层是基于红黑树的数据结构实现排序的, 增删改查性能都较好。
-
注意: TreeSet 集合是一定要排序的可以将元素按照指定的规则进行排序。
TreeSet集合默认的规则
-
对于数值类型: lnteger, Double, 官方默认按照大小进行升序排序。
-
对于字符串类型: 默认按照首字符的编号升序排序。
-
对于自定义类型如Student 对象, TreeSet 无法直接排序。
结论: 想要使用TreeSet 存储自定义类型需要制定排序规则
自定义排序规则
-
TreeSet 集合存储对象的的时候有2 种方式可以设计自定义比较规则
方式一
-
让自定义的类( 如学生类) 实现Comparable接口重写里面的compareTo方法来定制比较规则。
方式二
-
Treeset 集合有参数构造器, 可以设置Comparator 接口对应的比较器对象, 来定制比较规则。



可变参数
可变参数的作用
-
传输参数非常灵活, 方便。可以不传输参数, 可以传输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 {
public static List<Card> allCards = new ArrayList<>();
static {
String[] sizes = {"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2"};
String[] colors = {"♠", "♥", "♦", "♣"};
int index = 0;
for (String size : sizes) {
index++;
for (String color : colors) {
Card c = new Card(size, color, index);
allCards.add(c);
}
}
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) {
Collections.shuffle(allCards);
System.out.println("洗牌后:" + allCards);
List<Card> cafune = new ArrayList<>();
List<Card> dd = new ArrayList<>();
List<Card> dzh = new ArrayList<>();
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);
}
}
List<Card> lastThreeCards = allCards.subList(allCards.size() - 3, allCards.size());
sortCards(cafune);
sortCards(dd);
sortCards(dzh);
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) {
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 集合的完整格式:

Map集合体系


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);
maps.put("Huawei",1000);
maps.put("生活用品",10);
maps.put("手表",10);
System.out.println(maps);
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);
maps.put("Huawei",1000);
maps.put("生活用品",10);
maps.put("手表",10);
System.out.println(maps);
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);
}
}
}
Map 集合的遍历方式三: Lambda

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);
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);
}
});
}
}

HashMap 的特点
-
HashMap 是Map 里面的一个实现类。特点都是由键决定的: 无序、不重复、无索引
-
没有额外需要学习的特有方法, 直接使用Map 里面的方法就可以了。
-
HashMap 跟层原理是一模一样的, 都是哈希表结构, 只是HashMap 的每个元素包含两个值而已。
-
依赖hashCode方法和equals方法保证键的唯一
-
如果键要存储的是自定义对象,要重写hashcode和equals方法
-
基于哈希表,增删改查性能都比较好
实际上: set 系列集合的底层就是藺ap 实现的, 只是set 集合中的元素只要键数据, 不要值数据而已。
public HashSet() {
map = new HashMap<>();
}
{Student{name='无恙', age=22, sex=男}=上海, Student{name='周雄', age=20, sex=男}=广州}
LinkedHashMap 集合概述和特点
-
由键决定: 有序、不重复、无索引。
-
这里的有序指的是保证存储和取出的元素顺序一致
-
原理: 底层数据结构是依然哈希表, 只是每个键值对元素又额外的多了一个双链表的机制记录存储的顺序。

TreeMap 集合概述和特点
-
由键决定特性: 不重复、无索引、可排序
-
可排序: 按照键数据的大小默认升序( 有小到大) 排序。只能对键排序。
-
注意: TreeMap 集合是一定要排序的, 可以默认排序, 也可以将键按照指定的规则进行排序
-
TreeMap 跟TreeSet— 样底层原理是一样的。
TreeMap 集合自定义排序规则有两种
-
类实现Comparable接口,重写比较规则
-
集合自定义Comparator比较器对象,重写比较规则
Map 集合实现类特点
-
HashMap : 元素按照键是无序, 不重复, 无索引, 值不做要求, 基于哈希表( 与Map 体系一致)
-
LinkedHashMap: 元素按照键是有序, 不重复, 无索引, 值不做要求, 基于哈希表
-
TreeMap: 元素只能按照键排序, 不重复, 无索引的, 值不做要求, 可以做排序
集合嵌套
案例 统计投票人数

public class MapTest04 {
public static void main(String[] args) {
HashMap<String, List<String>> data = new HashMap<>();
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);
HashMap<String, Integer> infos = new HashMap<>();
Collection<List<String>> values = data.values();
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);
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构