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提供的常用静态方法
image
代码案例

学生类:
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()));
    }
}

案例:斗地主游戏
分析业务需求

  1. 总共54张牌
  2. 点数:"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2"
  3. 花色:"♠️", "♥️", "♣️", "♦️"
  4. 大小王:"🤡", "🃏"
  5. 斗地主:发出51张牌,剩下三张牌作为底牌。

分析实现

  1. 在启动游戏房间的时候,应该提前准备好54张牌
  2. 接着需要完成洗牌,发牌,对牌排序,看牌
    代码实现
扑克对象:(每一张都是独立的一个对象)
// 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集合体系
image

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

遍历方式

  1. 键找值:先获取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);
        }
    }
}
  1. 键值对:把"键值对"看成一个整体进行遍历(难度较大)
    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);
        }
    }
}
  1. 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集合案例-省和市
需求:要求在程序中记住如下省份和其对应的城市信息,记录成功后,要求可以查询出湖北省的城市信息。
image
案例代码

//目标:掌握集合的嵌套(重点)
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);
        }
    }
}
posted @ 2025-08-14 16:34  狂风将军  阅读(10)  评论(0)    收藏  举报