21Java基础之集合进阶(二)
Collection的其他相关知识
可变参数
- 就是一种特殊形参,定义在方法、构造器的形参列表里,格式是:数据类型……参数名称;
可变参数的特点和好处
- 特点:可以不传数据给它;可以传一个或者同时传多个数据给它;也可以传一个数组给它。
- 好处:常常用来灵活的接受数据
可变参数的注意事项:
- 可变参数在方法内部就是一个数组。
- 一个形参列表中可变参数只能有一个。
- 可变参数必须放在形参列表的最后面。
代码案例
//目标:掌握可变参数的使用
public class Test01 {
public static void main(String[] args) {
sum();// 可以不传参数
sum(10);//可以传一个参数
sum(10, 17);//可以传多个参数
sum(new int[] {10, 17, 32});//可以传一个数组
}
// 作用:接收数据非常灵活
//注意事项:
// 1.一个形参列表中,可变参数只能有一个
// 2.一个形参列表中,如果有多个参数,可变参数只能有一个,并且必须放在最后
public static void sum(int...nums){
// 本质:可变参数在方法内部本质就是一个数组
System.out.println("个数:" + nums.length);
System.out.println("内容:" + Arrays.toString(nums));
}
}
Collections工具类
- Collections是一个用来操作集合的工具类。
Collections提供的常用静态方法

代码案例
学生类:
public class Student implements Comparable{
private String name;
private int age;
private double height;
@Override
public int compareTo(Object o) {
return 0;
}
public Student() {
}
public Student(String name, int age, double height) {
this.name = name;
this.age = age;
this.height = height;
}
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 double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", height=" + height +
'}' + '\n';
}
}
测试:
//目标: 掌握Collections集合工具类的使用.
public class CollectionsDemo01 {
public static void main(String[] args) {
//1. public static <T> boolean addAll(Collection<T> c, T... elements):为集合批量添加数据
List<String> names = new ArrayList<>();
Collections.addAll(names, "张无忌", "赵敏", "小昭", "殷素素", "周芷若");
System.out.println(names);
//2. public static void shuffle(List<?> list):打乱集合中元素的顺序
Collections.shuffle(names);
System.out.println(names);
//3. public static <T> void sort(List<T> list):将集合中的元素按照升序排序
List<Student> students = new ArrayList<>();
Student s1 = new Student("张无忌", 20, 1.75);
Student s2 = new Student("殷素素", 39, 1.72);
Student s3 = new Student("赵敏", 18, 1.71);
Student s4 = new Student("小昭", 17, 1.65);
Collections.addAll(students, s1, s2, s3, s4);
//方式一:让对象的类实现comparable接口,重写compareTo方法.
Collections.sort(students);
System.out.println(students);
//方式二:在调用sort方法的时候,传递一个比较器对象.
Collections.sort(students, (o1, o2)->Double.compare(o2.getHeight(), o1.getHeight()));
}
}
案例:斗地主游戏
分析业务需求
- 总共54张牌
- 点数:"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2"
- 花色:"♠️", "♥️", "♣️", "♦️"
- 大小王:"🤡", "🃏"
- 斗地主:发出51张牌,剩下三张牌作为底牌。
分析实现
- 在启动游戏房间的时候,应该提前准备好54张牌
- 接着需要完成洗牌,发牌,对牌排序,看牌
代码实现
扑克对象:(每一张都是独立的一个对象)
// Lombok:使用注解简化get set 有参 无参构造器的写法
// IDEA版本 > 2022+
// Lombok版本 1.18.30+
@Data
@NoArgsConstructor // 无参构造器
@AllArgsConstructor // 有参构造器
public class Card {
private String number;
private String color;
private int size;
@Override
public String toString() {
return number + " " + color;
}
}
房间类:
// 房间对象
public class Room {
//1. 准备一副牌
private ArrayList<Card> cards = new ArrayList<>();
//2. 初始化54张牌
{
//3. 准备点数:个数确定,类型确定
String[] numbers = {"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2"};
//4. 准备花色:个数确定,类型确定
String[] colors = {"♠", "♥", "♣", "♦"};
//5. 准备大小王:个数确定,类型确定
String[] kings = {"🤡", "🃏"};
//6. 准备组装牌对象
int size = 0;
for (String number : numbers) {
size ++;
for (String color : colors) {
// 7. 创建对象封装这张牌
Card c = new Card(number, color, size);
// 5. 把这张牌添加到集合中
cards.add(c);
}
}
// 8. 把大小王添加到集合中
for (String king : kings) {
size ++;
Card c = new Card("", king, size);
cards.add(c);
}
System.out.println("新牌是:" + cards);
}
public void startGame() {
//9. 洗牌
Collections.shuffle(cards);
System.out.println("洗牌后:" + cards);
//10. 发牌
List<Card> player1 = new ArrayList<>();
List<Card> player2 = new ArrayList<>();
List<Card> player3 = new ArrayList<>();
for (int i = 0; i < cards.size() - 3; i++) {
Card c = cards.get(i);
if(i % 3 == 0){
player1.add(c);
}
else if(i % 3 == 1){
player2.add(c);
}
else if(i % 3 == 2){
player3.add(c);
}
}
// 拿到最后三张牌
List<Card> last_cards = cards.subList(cards.size() - 3, cards.size());
//抢地主
player1.addAll(last_cards);
//11. 对牌排序
sortCards(player1);
sortCards(player2);
sortCards(player3);
//12. 看牌
System.out.println("player1的牌是:" + player1);
System.out.println("player2的牌是:" + player2);
System.out.println("player3的牌是:" + player3);
}
private void sortCards(List<Card> cards) {
Collections.sort(cards, new Comparator<Card>() {
@Override
public int compare(Card o1, Card o2) {
return o2.getSize() - o1.getSize();
}
});
}
}
测试:
//目标:完成斗地主案例的实现
public class Test {
public static void main(String[] args) {
// 点数:"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2"
// 花色:"♠", "♥", "♣", "♦"
// 大小王:"🤡", "🃏"
//1. 每张牌是一个对象,设计一个牌类.
//2. 设计一个房间类,用于创建房间对象,开启游戏
Room room = new Room();
room.startGame();
}
}
输出结果:
新牌是:[3 ♠, 3 ♥, 3 ♣, 3 ♦, 4 ♠, 4 ♥, 4 ♣, 4 ♦, 5 ♠, 5 ♥, 5 ♣, 5 ♦, 6 ♠, 6 ♥, 6 ♣, 6 ♦, 7 ♠, 7 ♥, 7 ♣, 7 ♦, 8 ♠, 8 ♥, 8 ♣, 8 ♦, 9 ♠, 9 ♥, 9 ♣, 9 ♦, 10 ♠, 10 ♥, 10 ♣, 10 ♦, J ♠, J ♥, J ♣, J ♦, Q ♠, Q ♥, Q ♣, Q ♦, K ♠, K ♥, K ♣, K ♦, A ♠, A ♥, A ♣, A ♦, 2 ♠, 2 ♥, 2 ♣, 2 ♦, 🤡, 🃏]
洗牌后:[2 ♠, 5 ♣, A ♣, 2 ♥, Q ♥, 4 ♣, Q ♦, 🃏, J ♠, A ♥, 🤡, Q ♠, 5 ♦, Q ♣, 2 ♣, A ♠, 9 ♣, 8 ♣, 7 ♠, 8 ♠, A ♦, 10 ♠, 8 ♥, 4 ♠, 10 ♥, 3 ♦, 4 ♦, 7 ♦, 6 ♣, 5 ♥, J ♦, 6 ♦, J ♣, 3 ♥, 9 ♠, J ♥, 5 ♠, 2 ♦, 8 ♦, 10 ♣, 10 ♦, 3 ♠, 4 ♥, 7 ♣, K ♣, 9 ♦, 6 ♥, 6 ♠, K ♦, K ♥, K ♠, 9 ♥, 3 ♣, 7 ♥]
player1的牌是:[3 ♥, 3 ♣, 4 ♥, 5 ♦, 5 ♠, 7 ♠, 7 ♦, 7 ♥, 9 ♦, 9 ♥, 10 ♠, 10 ♥, 10 ♣, J ♦, Q ♦, K ♦, A ♥, A ♠, 2 ♠, 2 ♥]
player2的牌是:[3 ♦, 5 ♣, 6 ♣, 6 ♦, 6 ♥, 7 ♣, 8 ♠, 8 ♥, 9 ♣, 9 ♠, 10 ♦, Q ♥, Q ♣, K ♥, 2 ♦, 🤡, 🃏]
player3的牌是:[3 ♠, 4 ♣, 4 ♠, 4 ♦, 5 ♥, 6 ♠, 8 ♣, 8 ♦, J ♠, J ♣, J ♥, Q ♠, K ♣, K ♠, A ♣, A ♦, 2 ♣]
认识Map集合
- Map集合被称为双列集合,格式:{key1=value1, key2=value2, key3=value3, ...},一次需要存入一对数据作为一个元素。
- Map集合的每个元素"key=value"称为一个键值对/键值对对象/一个entry对象,Map集合又被称为"键值对集合"。
- Map集合的所有键是不允许重复的,但值可以重复,键和值是一一对应的,每个键只能找到自己对应的值。
Map集合在什么业务场景下使用
- 需要存储一一对应的数据时,就可以考虑使用Map集合来做。
Map集合体系

Map集合体系的特点
注意:Map系列集合的特点都是由键决定的,值只是一个附属品,值是不做要求的。
- HashMap(由键决定特点):无序、不重复、无索引;(用的最多)
- LinkedHashMap(由键决定特点):有序、不重复、无索引
- TreeMap(由键决定特点):按照大小默认升序排序、不重复、无索引。
案例代码
//目标:掌握Map集合的特点
public class MapDemo01 {
public static void main(String[] args) {
// Map体系整体特点:HashMap:按照键,无序,不重复,无索引。值不做要求,键和值都可以是null。
Map<String, Integer> map = new HashMap<>(); //多态,一行经典代码
map.put("JAVA从入门到入坟", 85);
map.put("华为手表", 43);
map.put("iphone15", 42);
map.put("雨伞", 20);
map.put("雨伞", 20);
map.put(null, null);
System.out.println(map);
}
}
常用方法
- Map是双列集合的祖宗,它的功能是全部双列集合都可以继承过来使用的。
Map的常用方法如下:
![image]()
代码案例
//目标:掌握Map集合的常用API(重点)
public class MapDemo02 {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("手表", 500);
map.put("iPhone", 4999);
map.put("huawei", 6999);
map.put("手办", 79);
map.put("Java入门", 128);
System.out.println(map);
//1. 获取集合的大小(元素个数)
System.out.println(map.size());
//2. 清空集合
/* map.clear();
System.out.println(map);*/
//3. 判断集合是否为空
System.out.println(map.isEmpty());
//4. 根据键获取对应的值(重点)
System.out.println(map.get("手表"));
System.out.println(map.get("手表2"));
//5. 根据键删除整个数据,返回删除数据对应的值(重点)
System.out.println(map.remove("手办"));
System.out.println(map);
//6. 判断是否包含某个键(重点)
System.out.println(map.containsKey("手办"));
System.out.println(map.containsKey("玩具"));
System.out.println(map.containsKey("手表"));
//7. 判断是否包含某个值
System.out.println(map.containsValue(4999));
System.out.println(map.containsValue(6999));
System.out.println(map.containsValue(129));
//8. 获取Map集合的全部键,到一个Set集合中返回
Set<String> keys = map.keySet();
System.out.println(keys);
//9. 获取Map集合的全部值:到一个Collection集合中返回.
Collection<Integer> values = map.values();
for (Integer value : values) {
System.out.println(value);
}
}
}
遍历方式
- 键找值:先获取map集合全部的键,再通过遍历键来找值。
- 需要用到的Map如下方法:
![image]()
//目标:掌握Map集合的遍历方式
public class MapDemo01 {
public static void main(String[] args) {
//1. 方式一:先找到键,再根据键找值
Map<String, Integer> map = new HashMap<>();
map.put("诺基亚", 2999);
map.put("iPhone", 4999);
map.put("huawei", 6999);
map.put("手办", 79);
System.out.println(map);
//1. 获取map集合的全部键
Set<String> keys = map.keySet();
//2. 根据键提取值
for (String key : keys){
Integer value= map.get(key);
System.out.println(key + "====>" + value);
}
}
}
- 键值对:把"键值对"看成一个整体进行遍历(难度较大)
![image]()
代码案例
//目标:掌握Map集合的遍历方式二:键值对遍历
public class MapDemo02 {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
map.put("蜘蛛精", 1000);
map.put("小龙女", 23);
map.put("慕婉晴", 31);
map.put("黄蓉", 18);
System.out.println(map);
//1. 将map集合转换为entry集合
Set<Map.Entry<String, Integer>> entries = map.entrySet();
//2. 遍历Entry集合
for (Map.Entry<String, Integer> entry : entries) {
String key = entry.getKey();
Integer value = entry.getValue();
System.out.println(key + ": " + value);
}
}
}
- Lambda:JDK1.8开始之后的新技术(非常简单)
- 需要用到Map的如下方法
![image]()
案例代码
//目标: 掌握Map集合的遍历方式三:lambda表达式
public class MapDemo03 {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
map.put("蜘蛛精", 1000);
map.put("小龙女", 23);
map.put("慕婉晴", 31);
map.put("黄蓉", 18);
System.out.println(map);
//方式3: lambda表达式
map.forEach(new BiConsumer<String, Integer>() {
@Override
public void accept(String s, Integer integer) {
System.out.println(s + ": " + integer);
}
});
System.out.println("-------------------------");
//简化lambda表达式
map.forEach((k, v) -> System.out.println(k + ": " + v));
}
}
Map集合的案例-统计投票人数
需求:某个班级80名学生,现在需要组织秋游活动,班长提供了四个景点依次是(A、B、C、D),每个学生只能选择一个景点,请统计出最终选哪个景点想去的人最多。
分析:
- 将准备80个学生选择的数据拿到程序中去,[A, A, B, B, A, C, D,...]
- 准备一个Map集合用于存储统计的结果,Map<String, Integer>, 键是景点,值代表投票数量。
- 遍历80个学生选择的景点,每遍历一个景点,就看Map集合中是否存在该节点,不存在存入“景点=1”,存在责对其对应值+1
注意:需要存储一一对应的数据时,就可以考虑使用Map集合来做。
案例代码
public class MapTest {
public static void main(String[] args) {
//1. 80个学生选出来的景点
String[] locations = {"玉龙雪山", "兵马俑", "西双版纳", "三亚"};
//2.定义一个list集合,存储80个学生随机挑选的想去的地方
List<String> data = new ArrayList<>();
Random r = new Random();
for(int i = 0; i < 80; i++){
data.add(locations[r.nextInt(locations.length)]);
}
System.out.println(data);
//3.定义Map集合:键存储景点,值存储票数
Map<String, Integer> loctionMap = new HashMap<>();
for (String datum : data) {
if(!loctionMap.containsKey(datum)){
loctionMap.put(datum, 1);
}
else{
int val = loctionMap.get(datum);
loctionMap.put(datum, ++val);
}
}
loctionMap.forEach((k,s)-> System.out.println(k + ": " + s));
}
}
HashMap集合的底层原理
- HashMap跟HashSet的底层原理是一模一样的,都是基于哈希表实现的。
实际上:原来学的Set系列集合的底层就是基于Map实现的,只是Set集合中的元素只要键数据,不要值数据而已。
- HashMap集合是一种增删改查数据,性能都较好的集合。
- 但是它是无序,不能重复,没有索引支持的(由键决定特点)
- HashMap的键依赖HashCode方法和equals方法保证键的唯一。
- 如果键存储是自定义类型的对象,可以通过重写hashCode和equals方法,这样可以保证多个对象内容一样时,HashMap集合就能认为是重复的。
LinkedHashMap集合的原理
- 底层数据结构依然是基于哈希表实现的,只是每个键值对元素又额外的多了一个双链表的机制记录元素顺序(保证有序)
实际上:原来学习的LinkedHashSet集合的底层原理就是LinkedHashMap。
![image]()
TreeMap
- 特点:不重复、无索引、可排序(按照键的大小默认升序排序,只能对键排序)
- 原理:TreeMap跟TreeSet集合的底层原理是一样的,都是基于红黑树实现的排序。
TreeMap集合同样也支持两种方式来指定排序规则
- 让类实现Comparable接口,重写比较规则。
- TreeMap集合有一个有参构造器,支持创建Comparator比较器对象,以便用来指定比较规则。
集合的嵌套
案例:Map集合案例-省和市
需求:要求在程序中记住如下省份和其对应的城市信息,记录成功后,要求可以查询出湖北省的城市信息。

案例代码
//目标:掌握集合的嵌套(重点)
public class MapTest04 {
public static void main(String[] args) {
//1. 定义一个Map集合存储全部省份和城市信息
Map<String, List<String>> provinces = new HashMap<>();
//2. 存入省份信息
List<String> cities1 = new ArrayList<String>();
Collections.addAll(cities1, "南京市", "无锡市", "徐州市", "常州市", "苏州市", "扬州市", "宿迁市");
List<String> cities2 = new ArrayList<String>();
Collections.addAll(cities2, "武汉市", "孝感市", "十堰市", "宜昌市", "鄂州市");
List<String> cities3 = new ArrayList<String>();
Collections.addAll(cities3, "石家庄市", "唐山市", "保定市", "邢台市", "张家口市");
provinces.put("江苏省",cities1);
provinces.put("湖北省",cities2);
provinces.put("河北省",cities3);
System.out.println(provinces);
//3. 遍历集合
List<String> cities = provinces.get("湖北省");
for (String city : cities) {
System.out.println(city);
}
}
}






浙公网安备 33010602011771号