Set和Map
Collections、Set、Map
// Collection总结
1. Collection
是单列集合的根接口
Collection
|-- List
|-- ArrayList
|-- LinkedList
|-- Vector
|-- Set
|-- HashSet
|-- TreeSet
add(E e)
clear()
remove(Object o)
contains(E e)
isEmpty()
size()
toArray()
2. Iterator
Collection : iterator() -> 获取集合对应的迭代器(Iterator)
Iterator :
hasNext() : 判断是否有下一个元素
next() : 获取下一个元素, 指针后移一位
增强for循环
for (元素的数据类型 变量名 : 容器) {}
3. 泛型
作用: 用来限定集合中元素的数据类型
好处:
1. 将问题从运行时期提前到编译时期
2. 省去了强转的麻烦
泛型类
泛型接口
泛型方法
修饰符 <T> 返回值类型 方法名(T t) {}
泛型的上下边界
<? extends E> : 泛型可以是E, 也可以是E的子类
<? super E> : 泛型可以是E, 也可以是E的父类
4. List
特点: 有序, 有索引, 可以重复
功能:
add(int index, E e)
remove(int index)
set(int index, E e)
get(int index)
ArrayList : 底层是数组, 查询快, 增删慢
LinkedList : 底层是链表, 查询慢, 增删快
addFirst(E e)
addLast(E e)
removeFirst()
removeLast()
getFirst()
getLast()
pop()
push()
5. 常见的数据结构
栈: 先进后出
队列: 先进先出
数组: 查询快, 增删慢
链表: 查询慢, 增删快
一. Set接口【理解】
List : 有序, 有索引, 可以重复
Set :
无序 : 存和取的顺序不一致
没有索引
不可以重复(保证元素的唯一)
Set集合的学习重点, 放在学习如何保证元素的唯一
1. HashSet
- 存储Java已经提供好的数据类型
- 可以保证元素的唯一
HashSet<String> set = new HashSet<>();
set.add("潮汐海灵");
set.add("潮汐海灵");
set.add("丽桑卓");
set.add("丽桑卓");
set.add("劫");
set.add("劫");
System.out.println(set); //[潮汐海灵, 劫, 丽桑卓]
2. HashSet存储自定义类型
如果存入元素的hashCode()返回值是不同, 元素会直接存入集合
如何存入元素的hashCode()和集合中已有的hashCode()相同, 会调用equals()进行比较
public class Demo03 {
public static void main(String[] args) {
// 存放Hero类型的Set集合
HashSet<Hero> set = new HashSet<>();
// 添加元素
set.add(new Hero("潮汐海灵", "大鲨鱼"));
set.add(new Hero("潮汐海灵", "大鲨鱼"));
set.add(new Hero("亚索", "哈萨key"));
set.add(new Hero("亚索", "哈萨key"));
set.add(new Hero("伊泽瑞尔", "精准弹幕"));
set.add(new Hero("伊泽瑞尔", "精准弹幕"));
// 遍历打印
for (Hero hero : set) {
System.out.println(hero); // 去重之后的结果
}
}
}
// =====================================================================
public class Hero {
private String name;
// 技能
private String skill;
public Hero() {
}
public Hero(String name, String skill) {
this.name = name;
this.skill = skill;
}
// get/set省略
/*
重写之后比较的是属性值
*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Hero hero = (Hero) o;
return Objects.equals(name, hero.name) &&
Objects.equals(skill, hero.skill);
}
/*
使用属性, 进行一系列的计算(为了属性不同, 让hashCode()一定不同), 最终得到一个值
*/
@Override
public int hashCode() {
return Objects.hash(name, skill);
}
@Override
public String toString() {
return "Hero{" +
"name='" + name + '\'' +
", skill='" + skill + '\'' +
'}';
}
}
总结: 如果使用HashSet集合存储自定义对象, 想保证元素的唯一, 需要重写hashCode()和equals()方法
3. LinkedHashSet
LinkedHashSet是HashSet集合的子类
Set集合中惟一一个有序的集合
特点
- 可以保证元素的唯一
- 由于是链表, 可以保证有序(怎么存就怎么取)
4. TreeSet
TreeSet集合能够排序, 是因为存入集合的元素自身具备比较功能
特点
- 可以保证元素的唯一
- 可以对元素进行从小到大的排序(自然排序)
5. 二叉树
(1) 基本概念
- 节点, 左子节点, 右子节点
- 二叉树
- 度
- 高度
- 二叉查找树(二叉搜索树, 二叉排序树)
- 平衡二叉树
(2) 左旋和右旋
-
左旋: 根节点的右侧往左拉, 原右子节点变成新的根节点, 多余的左节点当做原根节点的右子节点
-
右旋: 根节点的左侧往右拉, 原左子节点编程新的根节点, 多余的右节点当做原根节点的左子节点
详见资料中的图片
(3) TreeSet中的二叉树
二. Collections【会用】
用来操作集合的工具类
1. 可变参数
2. 常用功能
- addAll
public static <T> boolean addAll(Collection<T> c, T... elements)
将elements可变参数中的所有内容, 添加到c集合中
由于形参定义的是Collection集合, 随意这个方法可以添加所有Colleciton的子类(ArrayList, HashSet ...)
- shuffle
public static void shuffle(List<?> list) : 随机置换
注意: 只能操作List集合, 不能操作Set集合
- sort
public static <T extends Comparable<T>> void sort(List<T> list) :
排序
集合泛型的数据类型, 必须是Comparable的实现类
Comparable: 自然排序 -> 从小到大的升序排序
3. 比较器
public static <T> void sort(List<T> list, Comparator<T> c)
(1) 集合元素是Integer类型的排序
ArrayList<Integer> list = new ArrayList<>();
// 添加元素
list.add(70);
list.add(50);
list.add(20);
list.add(50);
list.add(40);
// 匿名内部类: new 接口名() {} -> 实现了该接口的实现类对象
Collections.sort(list, new Comparator<Integer>() {
/*
返回值的作用
1 - 2 -> 升序
2 - 1 -> 降序
*/
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
System.out.println(list);
(2) 集合元素是String类型的排序
package com.guang._03comparator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
/*
String字符串中比较器排序的使用
*/
public class Demo02 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "aaa", "b", "cc");
// 自然排序: 首字母升序
// Collections.sort(list);
// 首字母降序
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
// 字符串对象.compareTo(另一个字符串对象)
// return o1.compareTo(o2); // 字典顺序升序
// return o2.compareTo(o1); // 字典顺序降序
// return o1.length() - o2.length(); // 长度升序
return o2.length() - o1.length(); // 长度降序
}
});
System.out.println(list);
}
}
(3) 集合元素是自定义类型的简单排序
package com.guang._03comparator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public class Demo03 {
public static void main(String[] args) {
ArrayList<Hero> list = new ArrayList<>();
list.add(new Hero("a奈文摩尔", 1));
list.add(new Hero("b雷克萨", 3));
list.add(new Hero("b雷克萨", 4));
list.add(new Hero("c奥蕾莉亚", 2));
// 如果两种排序功能都存在, 使用比较器排序
// 不报错: Integer, String
Collections.sort(list, new Comparator<Hero>() {
@Override
public int compare(Hero o1, Hero o2) {
return o2.getName().compareTo(o1.getName());
}
});
System.out.println(list);
}
}
(4) 集合元素是自定义类型的进阶排序
package com.guang._03comparator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
/*
先按照姓名降序, 如果姓名相同, 按照排名升序
*/
public class Demo04 {
public static void main(String[] args) {
ArrayList<Hero> list = new ArrayList<>();
list.add(new Hero("a奈文摩尔", 1));
list.add(new Hero("b雷克萨", 4));
list.add(new Hero("b雷克萨", 2));
list.add(new Hero("b雷克萨", 3));
list.add(new Hero("b雷克萨", 1));
list.add(new Hero("c奥蕾莉亚", 2));
Collections.sort(list, new Comparator<Hero>() {
@Override
public int compare(Hero o1, Hero o2) {
// 如果姓名相同, 比较rank
// o2.getName().compareTo(o1.getName() 返回 0, 说明姓名相同
int num = o2.getName().compareTo(o1.getName());
/*if (num == 0) {
return o1.getRank() - o2.getRank();
} else { // 姓名不同
return num;
}*/
return num == 0 ? o1.getRank() - o2.getRank() : num;
}
});
System.out.println(list);
}
}
(5) TreeSet也可以使用比较器
public TreeSet(Comparator<? super E> comparator)
有Studnet类 [name:String , age:int, math:int, english:int, chinese:int, totalScore:int]
- 先按照总成绩降序排序
- 总成绩相同, 按照语文降序
- 总成绩和语文都相同, 按照数学降序
- 总成绩, 语文, 数学相同, 按照年龄降序
对应的练习demo案例:
1、创建对象:
public class Student {
private String name;
private Integer age;
private Integer math;
private Integer english;
private Integer chinese;
private Integer totalScore;
// 无参、有参、getter/setter和toString方法省略(注意修改一个getTotal的值)
}
测试代码:
public class StudentDemo {
public static void main(String[] args) {
List<Student> studentList = new ArrayList<>();
studentList.add(new Student("1", 22, 66, 33, 76, 56));
studentList.add(new Student("2", 11, 16, 77, 88, 99));
studentList.add(new Student("3", 9, 82, 99, 99, 34));
studentList.add(new Student("4", 17, 33, 77, 88, 99));
// 这里来使用函数式接口比较好
Collections.sort(studentList, (s1, s2) -> {
// s1-s2----->升序
// s2-s1----->降序
/**
* 1. 先按照总成绩降序排序
* 2. 总成绩相同, 按照语文降序
* 3. 总成绩和语文都相同, 按照数学降序
* 4. 总成绩, 语文, 数学相同, 按照年龄降序
*/
int var1 = s2.getTotalScore() - s1.getTotalScore();
int var2 = var1 == 0 ? s2.getChinese() - s1.getChinese() : var1;
int var3 = var2 == 0 ? s2.getMath() - s1.getMath() : var2;
int var4 = var3 == 0 ? s2.getAge() - s1.getAge() : var3;
return var4;
});
for (Student student : studentList) {
System.out.println(student);
}
/**
* Student{name='3', age=9, math=82, english=99, chinese=99, totalScore=280}
* Student{name='4', age=17, math=33, english=77, chinese=88, totalScore=198}
* Student{name='2', age=11, math=16, english=77, chinese=88, totalScore=181}
* Student{name='1', age=22, math=66, english=33, chinese=76, totalScore=175}
*/
}
}
注意看一下:
math的成绩成立的前提之下,是按照总成绩、语文降序的前提之下进行排序的;
age是在总成绩、语文、数学分数相同的前提下,进行排序的;
三. Map集合【重点】
1. Map集合的特点
- 将键映射到值的对象, 双列集合, 以键值对的形式存在
- Map集合中的键是唯一的
- 一个键最多对应一个值(可能没有值(null), 一个值)
键值对形式存在
键是唯一的
2. 双列集合的继承体系
3. Map中的常用方法
Map<K,V>
K : key 键
V : value 值
(1) 增删改查
- 增加/修改
V put(K key, V value) : 向集合中添加一组键值对,
如果键相同, 值覆盖
返回值: 返回的是替换掉的值
- 删除
V remove(Object key) : 删除指定键对应的, 键值对
返回值: 返回被删除掉的值
- 查询
V get(Object key) : 根据键, 获取值
(2) 其他操作
- 长度
int size() : 获取集合中, 键值对 的个数
- 判断是否包含
boolean containsKey(Object key) : 判断集合中是否包含指定键
boolean containsValue(Object value) : 判断集合中是否包含指定值
- 获取所有键
Set<K> keySet() : 获取Map集合中所有键存在的Set集合
返回值: 返回存放所有键的Set集合
- 获取所有值
Collection<V> values() : 获取Map集合中所有值存在的Colletion集合
4. Map集合的遍历
(1) 键找值
- 获取所有的键
- 通过键获取值
HashMap<String, String> map = new HashMap<>();
map.put("C罗", "葡萄牙");
map.put("梅西", "阿根廷");
map.put("德布劳内", "比利时");
map.put("内马尔", "巴西");
map.put("武磊", "中国");
// 获取所有键 -> Set<K>
Set<String> keySet = map.keySet();
// 遍历Set集合, 获取每一个键
for (String key : keySet) {
// 根据键获取值
String value = map.get(key);
System.out.println(key + "=" + value);
}
(2) 键值对
Set<Map.Entry<K,V>> entrySet() : 获取所有键值对 所在的 Set集合
Set<键值对>
HashMap<String, String> map = new HashMap<>();
map.put("C罗", "葡萄牙");
map.put("梅西", "阿根廷");
map.put("德布劳内", "比利时");
map.put("内马尔", "巴西");
map.put("武磊", "中国");
// Set集合存放String -> Set<String>
// Set集合中存放的是 键值对
Set<Map.Entry<String, String>> entrySet = map.entrySet();
// 遍历Set集合, 获取每一个键值对
// Map.Entry<String, String> -> 键值对
for (Map.Entry<String, String> entry : entrySet) {
// 键值对类型中有两个功能: getKey(), getValue()
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key + "=" + value);
}
5. HashMap存储自定义类型
如果HashMap存储自定义对象作为键, 想保证键的唯一, 需要重写hashCode()和equals()
6. LinkedHashMap
特点
- 保证键的唯一
- 有序(怎么存就怎么取)
7. TreeMap
特点
- 保证键的唯一
- 可以根据键进行排序
8. 集合的嵌套
- ArrayList集合嵌套HashMap
package com.guang._04map;
import java.util.ArrayList;
import java.util.HashMap;
public class Demo07 {
public static void main(String[] args) {
// 创建ArrayList集合, 集合中存放HashMap
ArrayList<HashMap<String , String>> list = new ArrayList<>();
// 创建HashMap
HashMap<String, String> map1 = new HashMap<>();
map1.put("姓名", "张三");
map1.put("年龄", "23");
map1.put("性别", "男");
HashMap<String, String> map2 = new HashMap<>();
map2.put("姓名", "李四");
map2.put("年龄", "24");
map2.put("性别", "女");
HashMap<String, String> map3 = new HashMap<>();
map3.put("姓名", "王五");
map3.put("年龄", "25");
map3.put("性别", "男");
// 将Map集合添加到List中
list.add(map1);
list.add(map2);
list.add(map3);
// 遍历List集合, 获取到每一个元素, 元素是HashMap类型
for (HashMap<String, String> map : list) {
System.out.println("~~~~~~~~~~~~~");
// 遍历HashMap
for (String key : map.keySet()) {
System.out.println(key + "=" + map.get(key));
}
}
}
}
9. Map集合的练习
- 需求: 键盘录入一个字符串, 获取字符串中每一个字符出现的次数
- 键盘录入: abcdabcdab -> a(3)b(3)c(2)d(2)
public class MapTest {
public static void main(String[] args) {
Map<Character,Integer> stringIntegerMap = new HashMap<>();
String tmp = "adfsafcafdsafdsa";
char[] chars = tmp.toCharArray();
for (char var0 : chars) {
stringIntegerMap.put(var0,!stringIntegerMap.containsKey(var0)?1:stringIntegerMap.get(var0)+1);
}
// 遍历输出
StringBuilder stringBuilder = new StringBuilder();
for (Map.Entry<Character, Integer> var1 : stringIntegerMap.entrySet()) {
Character var2 = var1.getKey();
Integer var3 = var1.getValue();
stringBuilder.append(var2+"("+var3+")");
}
System.out.println(stringBuilder);
}
}
四. 总结
很多set的底层都是给予map来进行实现的。所以这部分很有必要来对齐进行讲清楚。
set的最大特性是为了保持有序,而map的特性是给予引用类型重写的hashCode和equals方法来进行实现的。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?