双列集合

发布时间 2023-09-09 11:54:27作者: SimpleWord
title: 双列集合
index_img: img/21.svg
tags:
  - Java SE
  - 集合
categories:
  - Java SE
hide: false
excerpt: 原理、常用方法、遍历

双列集合

由一对对元素组成{key-value}的集合。

  • 一次存一对数据
  • key不能重复【元素对唯一】,value可以
  • key和value是一一映射关系
  • {key-value}称为键值对(对象),再java中叫做Entry对象

双列集合接口Map

常用方法

方法名称 说明
V put(K key, V value) 添加元素(元素存在【看key】就会覆盖)
V remove(Object key) 根据键删除键值对元素,返回value或null
clear() 移除所有的键值对元素
boolean containsKey(Object key) 判断集合是否包含指定的
boolean containsValue(Object value) 判断集合是否包含指定的
boolean isEmpty() 判断集合是否为空
size() 集合的长度,也就是集合中键值对的个数
public class Main {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        map.put("张三", 12); //添加和覆盖数据
        map.put("李斯", 23);
        map.put("李斯", 32); //key相同覆盖前一个
        map.put("Tom", 12);
        System.out.println(map);

        System.out.println("--------------");
        if (map.containsKey("张三")) {
            map.remove("张三");
        }
        System.out.println(map);

        System.out.println("--------------");
        if (map.isEmpty()) {
            System.out.println("map空");
        } else {
            System.out.println(map.size());
        }
    }
}

image-20230809130120199

遍历

键找值

  • 键是唯一的,可以根据键找值
  • 将键放入单列集合,然后遍历【单列集合】
public class Main {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        map.put("Tom", 100);
        map.put("Lay", 90);
        map.put("kup", 90);
        map.put("duk", 0);

        Set<String> keys = map.keySet();  //将键放在单列集合中

        //forEach
        keys.forEach(s -> {
            System.out.println("{" + s + "-" + map.get(s) + "}"); //通过map集合的get(key)获取Value
        });

        //增强for
        System.out.println("-------------");
        for (String s : keys) {
            System.out.println("{" + s + "-" + map.get(s) + "}");
        }

        //迭代器
        System.out.println("-------------");
        Iterator<String> it = keys.iterator();
        while (it.hasNext()) {
            String s = it.next();
            System.out.println("{" + s + "-" + map.get(s) + "}");
        }
    }
}

image-20230809131448871

键值对

获取键值对对象,放入单列集合,遍历【单列集合】

public class Main {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        map.put("Tom", 100);
        map.put("Lay", 90);
        map.put("kup", 90);
        map.put("duk", 0);

        Set<Map.Entry<String, Integer>> entries = map.entrySet(); //Entry是Map中定义的一个接口,用于管理键值对set对象

        entries.forEach(s -> System.out.println("{" + s.getKey() + "-" + s.getValue() + "}"));

        System.out.println("------------------");
        for (Map.Entry<String, Integer> s : entries) {
            System.out.println("{" + s.getKey() + "-" + s.getValue() + "}");
        }

        System.out.println("----------------");
        Iterator<Map.Entry<String, Integer>> it = entries.iterator();
        while (it.hasNext()) {
            Map.Entry<String, Integer> s = it.next();
            System.out.println("{" + s.getKey() + "-" + s.getValue() + "}");
        }
    }
}

image-20230809132939577

forEach遍历Map

  • 这里底层使用的是键值对方式
public class Main {
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();
        map.put("李白", "天生我材必有用");
        map.put("杜甫", "国破山河在");
        map.put("李清照", "不肯过江东");

        map.forEach(new BiConsumer<String, String>() {
            @Override
            public void accept(String key, String value) {
                System.out.println("{" + key + "-" + value + "}");
            }
        });

        System.out.println("------------------");
        map.forEach((key, value) -> System.out.println("{" + key + "-" + value + "}"));
    }
}

image-20230809134325845

双列集合实现类

HashMap

  • 无序、不重复【key决定的】、无索引
  • 底层结构与HashSet一样(数组、链表、红黑树),但规则有些不同
    • 使用的只是Key的哈希值【自定义对象只需要重写key的hashCode和equals即可】
    • 哈希值一样,是直接覆盖,而不是丢弃不存
    • 哈希值不一样是与HashSet一样
    • 当HashMap中的元素数量超过了负载因子(默认为0.75)与当前容量的乘积时,HashMap就会进行扩容。扩容后的容量是原来的两倍【新建数组】。

如此:

通过重写equalshashCode来保证Key的唯一性

  • 如果键是自定义对象【如学生】需要两个方法都重写【非自定义对象,java写好了】
  • 如果值是自定义对象,都不需要重写

问题描述

创建一个HashMap集合,【重写】是学生对象(Student),值是籍贯(String)

  • 存储三个键值对元素,并遍历
  • 要求:同姓名,同年龄认为是同一个学生【重写】
    • 可以通过的新学生对象否覆盖来判断
public class Main {
    public static void main(String[] args) {
        HashMap<Student, String> stu = new HashMap<>();

        Student stu1 = new Student("张三", 18);
        Student stu2 = new Student("李四", 8);
        Student stu3 = new Student("王五", 12);
        Student stu4 = new Student("王五", 12);

        stu.put(stu1, "云南");
        stu.put(stu2, "江苏");
        stu.put(stu3, "浙江");
        stu.put(stu4, "黑龙江"); //重写则覆盖stu3【已经重写】,否则会由两个王五,不符合要求

        //键找值遍历
        Set<Student> set = stu.keySet();
        set.forEach(s -> System.out.println("{" + s + "-" + stu.get(s) + "}"));
        System.out.println("---------");

        //键值对遍历
        for (Map.Entry<Student, String> s : stu.entrySet()) {
            System.out.println("{" + s.getKey() + "-" + s.getValue() + "}");
        }
        System.out.println("---------");

        //map的forEach
        stu.forEach((key, value) -> System.out.println("{" + key + "-" + value + "}"));
    }
}

image-20230809141234414

某个班级80名学生,现在需要组成秋游活动,班长提供了四个景点依次是(A、B、C、D),每个学生只能选择一个景点,请统计出最终哪个景点想去的人数最多。

  • 可以定义为map集合
  • 判断收集到的数据中,key是否存在
    • 存在,数目+1,覆盖
    • 不存在,新建
public class Main {
    public static void main(String[] args) {
        //投票的数据
        String[] arr = {"A", "B", "C", "D"}; //存储键,也可以使用单列集合

        //模拟投票和存储
        Random random = new Random();
        ArrayList<String> list = new ArrayList<>();//存储80个投票的结果【ABCD之间】
        for (int i = 0; i < 80; i++) {
            int index = random.nextInt(arr.length); //再【0~4)之间生成数字,模拟投票的选择在ABCD之间
            list.add(arr[index]);
        }

        //统计规则:使用map适应性比计数器更好
        HashMap<String, Integer> map = new HashMap<>();
        for (String key : list) {
            if (map.containsKey(key)) {
                //当前key存在,获取先前票数【Value】,然后++,然后覆盖
                int count = map.get(key);
                count++;
                map.put(key, count);
            } else {
                //当前key不存在
                map.put(key, 1);
            }
        }

        //打印统计信息
        System.out.println(map);

        //统计投票信息,求最大值(可能由多个)
        int max = 0; //以0为基准
        for (Map.Entry<String, Integer> kv : map.entrySet()) {
            int num = kv.getValue();
            if (num > max) {
                max = num;
            }
        }

        System.out.println("max:" + max);

        //如果由多个最大值,需要都打出
        for (Map.Entry<String, Integer> skv : map.entrySet()) {
            int num = skv.getValue();
            if (num == max) {
                System.out.println("max:" + num);
            }
        }
    }
}

image-20230809144452337

LinkedHashMap

  • 有序、不重复【key】、无索引
    • 存取有序
  • 底层结构,哈希表+双链表存储存取顺序
public class Main {
    public static void main(String[] args) {
        LinkedHashMap<String, String> linkMap = new LinkedHashMap<>();
        linkMap.put("C", "1");
        linkMap.put("D", "2");
        linkMap.put("A", "3");
        linkMap.put("F", "4");
        linkMap.forEach((key, value) -> System.out.println("{" + key + "-" + value + "}")); //有序
    }
}

image-20230809145426891

TreeMap

TreeMap跟TreeSet底层原理一样,都是红黑树结构的。

由键决定特性:不重复、无索引、可排序【键排序】

自定义排序依旧可以使用两种方法(参考TreeSet)

问题描述

键:整数表示id
值:字符串表示商品名称
要求:按照id的升序排列

方法1:javabean实现Compareable接口

public class Goods implements Comparable<Goods> {

    @Override
    public int compareTo(Goods goods) {
        return this.ID - goods.ID;
    }
  	重写hashCode和equals
    ...
      
public class Main {
    public static void main(String[] args) {
        Goods goods1 = new Goods("小吃1", 5);
        Goods goods2 = new Goods("小吃2", 3);
        Goods goods3 = new Goods("小吃3", 4);
        Goods goods4 = new Goods("小吃4", 1);

        TreeMap<Goods, String> treeMap = new TreeMap<>();
        treeMap.put(goods1, "小吃区");
        treeMap.put(goods2, "小吃区");
        treeMap.put(goods3, "小吃区");
        treeMap.put(goods4, "小吃区");

        treeMap.forEach((key, value) -> System.out.println("[" + key + "-" + value + "]"));
    }
}

image-20230809151235015

方法2:javabean实现Comparator接口

  • 这里我们用降序来区分
public class Main {
    public static void main(String[] args) {
        Goods goods1 = new Goods("小吃1", 5);
        Goods goods2 = new Goods("小吃2", 3);
        Goods goods3 = new Goods("小吃3", 4);
        Goods goods4 = new Goods("小吃4", 1);

        TreeMap<Goods, String> treeMap = new TreeMap<>((s1,s2)->Integer.compare(s2.ID,s1.ID)); //注意
        treeMap.put(goods1, "小吃区");
        treeMap.put(goods2, "小吃区");
        treeMap.put(goods3, "小吃区");
        treeMap.put(goods4, "小吃区");

        treeMap.forEach((key, value) -> System.out.println("[" + key + "-" + value + "]"));
    }
}

image-20230809151445720