Java集合【超详细】2 -- Map、可变参数、Collections类

news2025/1/21 1:01:24

文章目录

  • 一、Map集合
    • 1.1 Map集合概述和特点【理解】
    • 1.2 Map集合的基本功能【应用】
    • 1.3 Map集合的获取功能【应用】
    • 1.4 Map集合的两种遍历方式
  • 二、HashMap集合
    • 2.1 HashMap集合概述和特点【理解】
    • 2.2 HashMap的组成、构造函数
    • 2.3 put、查找方法
    • 2.4 HashMap集合应用案例【应用】
  • 三、TreeMap集合
    • 3.1 TreeMap集合概述和特点【理解】
    • 3.2 TreeMap集合应用案例【应用】
  • 四、可变参数
  • 五、Collections类
    • 5.1 Collections常用功能
    • 5.2 Comparator比较器
  • 六、综合练习
    • 练习1:随机点名器
    • 练习2:带概率的随机
    • 练习3:随机不重复
    • 练习4:集合的嵌套

还记得 Java集合框架体系、Collection、List、ArrayList、LinkedList、Set、TreeSet、HashSet 吗?如果忘记可以到这里重新温习: Java集合【超详细】

本文我们重点介绍 Map、HahsMap、TreeMap相关集合。Collection、List、Set相关部分可查看 Java集合【超详细】。

一、Map集合

在这里插入图片描述

1.1 Map集合概述和特点【理解】

  • Map集合概述

    interface Map<K,V>  K:键的类型;V:值的类型
    
  • Map集合的特点

    • 双列集合,一个键对应一个值
    • 键不可以重复,值可以重复
  • Map集合的基本使用

public class MapDemo01 {
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();
        //V put(K key, V value) 将指定的值与该映射中的指定键相关联
        map.put("student1", "zhangsan");
        map.put("student2", "lisi");
        map.put("student3", "wangwu");
        map.put("student4", "zhaoliu");

        //输出集合对象
        //{student2=lisi, student1=zhangsan, student4=zhaoliu, student3=wangwu}
        System.out.println(map);
    }
}

1.2 Map集合的基本功能【应用】

  • 方法介绍
方法名说明
V put(K key,V value)添加元素
V remove(Object key)根据键删除键值对元素
void clear()移除所有的键值对元素
boolean containsKey(Object key)判断集合是否包含指定的键
boolean containsValue(Object value)判断集合是否包含指定的值
boolean isEmpty()判断集合是否为空
int size()集合的长度,也就是集合中键值对的个数
  • 示例代码
public class MapDemo02 {
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();
        //V put(K key, V value) 将指定的值与该映射中的指定键相关联
        map.put("student1", "zhangsan");
        map.put("student2", "lisi");
        map.put("student3", "wangwu");
        map.put("student4", "zhaoliu");

//        System.out.println(map.remove("student1")); //zhangsan
//        System.out.println(map.remove("student5")); //null
//        //{student2=lisi, student4=zhaoliu, student3=wangwu}
//        System.out.println(map);

//        map.clear();
//        System.out.println(map.size() + ",  " + map);  //0,  {}

        System.out.println(map.containsKey("student1"));   //true
        System.out.println(map.containsKey("student5"));   //false
        System.out.println(map.containsValue("zhangsan")); //true
        System.out.println(map.containsValue("Jenny"));    //false
        
        System.out.println(map.isEmpty());  //false
        System.out.println(map.size());     //4

        System.out.println(map);
    }
}

1.3 Map集合的获取功能【应用】

  • 方法介绍
方法名说明
V get(Object key)根据键获取值
Set keySet()获取所有键的集合
Collection values()获取所有值的集合
Set<Map.Entry<K,V>> entrySet()获取所有键值对对象的集合
  • 示例代码
public class MapDemo03 {
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();
        map.put("student1", "zhangsan");
        map.put("student2", "lisi");
        map.put("student3", "wangwu");
        map.put("student4", "zhaoliu");

        System.out.println(map.get("student1"));
        System.out.println(map.get("student5"));

        //Set<K> keySet():获取所有键的集合
        Set<String> keySet = map.keySet();
        for (String key : keySet) {
            System.out.println(key);
        }
        System.out.println("~~~~~");

        //Collection<V> values():获取所有值的集合
        Collection<String> values = map.values();
        for (String value : values) {
            System.out.println(value);
        }
        System.out.println("~~~~~");

        Set<Map.Entry<String, String>> entries = map.entrySet();
        for (Map.Entry<String, String> entry : entries) {
            System.out.println(entry);
        }
        System.out.println(entries);
    }
}

在这里插入图片描述

1.4 Map集合的两种遍历方式

Map集合有两种遍历方式

  • 方式一

    • 获取所有键的集合。用keySet()方法实现
    • 遍历键的集合,获取到每一个键。用增强for实现
    • 根据键去找值。用get(Object key)方法实现
  • 方式二

    • 获取所有键值对对象的集合
      • Set<Map.Entry<K,V>> entrySet():获取所有键值对对象的集合
    • 遍历键值对对象的集合,得到每一个键值对对象
      • 用增强for实现,得到每一个Map.Entry
    • 根据键值对对象获取键和值
      • 用getKey()得到键
      • 用getValue()得到值
public class MapDemo04 {
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();
        map.put("student1", "zhangsan");
        map.put("student2", "lisi");
        map.put("student3", "wangwu");
        map.put("student4", "zhaoliu");

        //方式一:获取所有键的集合。用keySet()方法实现
        Set<String> keySet = map.keySet();
        for (String key : keySet) {
            //根据键去找值。用get(Object key)方法实现
            String value = map.get(key);
            System.out.println(key + "," + value);
        }

        System.out.println("~~~~~");

        //方式二:获取所有键值对对象的集合
        Set<Map.Entry<String, String>> entries = map.entrySet();
        for (Map.Entry<String, String> entry : entries) {
            //根据键值对对象获取键和值
            String key = entry.getKey();
            String value = entry.getValue();
            System.out.println(key + "," + value);
        }
    }
}

二、HashMap集合

2.1 HashMap集合概述和特点【理解】

  • HashMap底层是哈希表结构的
  • 依赖hashCode方法和equals方法保证键的唯一
  • 如果键要存储的是自定义对象,需要重写hashCode和equals方法

HashMap存放数据的数据是什么呢?代码中存放数据的容器如下:

transient Node<K,V>[] table;

说明了该容器中是一个又一个node组成,而node有三种实现,所以hashMap中存放的node的形式既可以是Node也可以是TreeNode。

在这里插入图片描述

2.2 HashMap的组成、构造函数

  • HashMap的组成
public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {

    private static final long serialVersionUID = 362498820763181265L;
    //是hashMap的最小容量16,容量就是数组的大小也就是变量,transient Node<K,V>[] table。
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
    //最大数量,该数组最大值为2^31一次方。
    static final int MAXIMUM_CAPACITY = 1 << 30;
    //默认的加载因子,如果构造的时候不传则为0.75
    static final float DEFAULT_LOAD_FACTOR = 0.75f;
    //一个位置里存放的节点转化成树的阈值,也就是8,比如数组里有一个node,这个node链表的长度达到该值才会转化为红黑树。
    static final int TREEIFY_THRESHOLD = 8;
    //当一个反树化的阈值,当这个node长度减少到该值就会从树转化成链表
    static final int UNTREEIFY_THRESHOLD = 6;
    //满足节点变成树的另一个条件,就是存放node的数组长度要达到64
    static final int MIN_TREEIFY_CAPACITY = 64;
    //具体存放数据的数组
    transient Node<K,V>[] table;
    //entrySet,一个存放k-v缓冲区
    transient Set<Map.Entry<K,V>> entrySet;
    //size是指hashMap中存放了多少个键值对
    transient int size;
    //对map的修改次数
    transient int modCount;
    //The next size value at which to resize (capacity * load factor).
    int threshold;
    //加载因子
    final float loadFactor;
    
    //...
}
  • HashMap的构造函数
//只有容量,initialCapacity
public HashMap(int initialCapacity) {
    this(initialCapacity, DEFAULT_LOAD_FACTOR);
}

public HashMap() {
    this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}

public HashMap(Map<? extends K, ? extends V> m) {
    this.loadFactor = DEFAULT_LOAD_FACTOR;
    putMapEntries(m, false);
}

final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
    int s = m.size();
    if (s > 0) {
        if (table == null) { // pre-size
            float ft = ((float)s / loadFactor) + 1.0F;
            int t = ((ft < (float)MAXIMUM_CAPACITY) ?
                     (int)ft : MAXIMUM_CAPACITY);
            if (t > threshold)
                threshold = tableSizeFor(t);
        }
        else if (s > threshold)
            resize();
        for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
            K key = e.getKey();
            V value = e.getValue();
            putVal(hash(key), key, value, false, evict);
        }
    }
}

public HashMap(int initialCapacity, float loadFactor) {
    if (initialCapacity < 0)   // 容量不能为负数
        throw new IllegalArgumentException("Illegal initial capacity: " +
                                           initialCapacity);
    //当容量大于2^31就取最大值1<<31;
    if (initialCapacity > MAXIMUM_CAPACITY)
        initialCapacity = MAXIMUM_CAPACITY;
    if (loadFactor <= 0 || Float.isNaN(loadFactor))
        throw new IllegalArgumentException("Illegal load factor: " +
                                           loadFactor);
    this.loadFactor = loadFactor;
    //当前数组table的大小,一定是是2的幂次方
    // tableSizeFor保证了数组一定是是2的幂次方,是大于initialCapacity最接近的值。
    this.threshold = tableSizeFor(initialCapacity);
}

tableSizeFor()方法保证了数组大小一定是是2的幂次方,是如何实现的呢?

static final int tableSizeFor(int cap) {
    int n = cap - 1;
    n |= n >>> 1;
    n |= n >>> 2;
    n |= n >>> 4;
    n |= n >>> 8;
    n |= n >>> 16;
    return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}

该方法将一个二进制数第一位1后边的数字全部变成1,然后再加1,这样这个二进制数就一定是100…这样的形式。此处实现在ArrayDeque的实现中也用到了类似的方法来保证数组长度一定是2的幂次方。

2.3 put、查找方法

  • put方法

开发人员使用的put方法

public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

HashMap内部使用的put值的方法:

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    //当hash到的位置,该位置为null的时候,存放一个新node放入 
    // 这儿p赋值成了table该位置的node值
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    else {
        Node<K,V> e; K k;
        //该位置第一个就是查找到的值,将p赋给e
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
        //如果是红黑树,调用红黑树的putTreeVal方法 
        else if (p instanceof TreeNode)
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        else {
          //是链表,遍历,注意e = p.next这个一直将下一节点赋值给e,直到尾部,注意开头是++binCount
            for (int binCount = 0; ; ++binCount) {
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);
                    //当链表长度大于等于7,插入第8位,树化
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab, hash);
                    break;
                }
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
            }
        }
        if (e != null) { // existing mapping for key
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            return oldValue;
        }
    }
    ++modCount;
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}
  • 查找方法
final Node<K,V> getNode(int hash, Object key) {
    Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
    //先判断表不为空
    if ((tab = table) != null && (n = tab.length) > 0 &&
        //这一行是找到要查询的Key在table中的位置,table是存放HashMap中每一个Node的数组。
        (first = tab[(n - 1) & hash]) != null) {
        //Node可能是一个链表或者树,先判断根节点是否是要查询的key,就是根节点,方便后续遍历Node写法并且
        //对于只有根节点的Node直接判断
        if (first.hash == hash && // always check first node
            ((k = first.key) == key || (key != null && key.equals(k))))
            return first;
        //有子节点
        if ((e = first.next) != null) {
            //红黑树查找
            if (first instanceof TreeNode)
                return ((TreeNode<K,V>)first).getTreeNode(hash, key);
            do {
                //链表查找
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    return e;
            }
            //遍历链表,当链表后续为null则推出循环
            while ((e = e.next) != null);
        }
    }
    return null;
}

2.4 HashMap集合应用案例【应用】

  • 案例需求

    • 创建一个HashMap集合,键是学生对象(Student),值是居住地 (String)。存储多个元素,并遍历。
    • 要求保证键的唯一性:如果学生对象的成员变量值相同,我们就认为是同一个对象
  • 代码实现

学生类

public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age && Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

测试类

public class HashMapDemo {
    public static void main(String[] args) {
        HashMap<Student, String> hm = new HashMap<Student, String>();
        Student s1 = new Student("林青霞", 30);
        Student s2 = new Student("张曼玉", 35);
        Student s3 = new Student("王祖贤", 33);
        Student s4 = new Student("王祖贤", 33);

        //把学生添加到集合
        hm.put(s1, "西安");
        hm.put(s2, "武汉");
        hm.put(s3, "郑州");
        hm.put(s4, "北京");

        Set<Student> keySet = hm.keySet();
        for (Student key : keySet) {
            String value = hm.get(key);
            System.out.println(key.getName() + "," + key.getAge() + "," + value);
        }
    }
}

三、TreeMap集合

3.1 TreeMap集合概述和特点【理解】

  • TreeMap底层是红黑树结构
  • 依赖自然排序或者比较器排序,对键进行排序
  • 如果键存储的是自定义对象,需要实现Comparable接口或者在创建TreeMap对象时候给出比较器排序规则

3.2 TreeMap集合应用案例【应用】

  • 案例需求

    • 创建一个TreeMap集合,键是学生对象(Student),值是籍贯(String),学生属性姓名和年龄,按照年龄进行排序并遍历
    • 要求按照学生的年龄进行排序,如果年龄相同则按照姓名进行排序
  • 代码实现

学生类

public class Student implements Comparable<Student>{
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

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

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public int compareTo(Student o) {
        //按照年龄进行排序
        int result = o.getAge() - this.getAge();
        //次要条件,按照姓名排序。
        result = result == 0 ? o.getName().compareTo(this.getName()) : result;
        return result;
    }
}

测试类

public class TreeMapDemo {
    public static void main(String[] args) {
        TreeMap<Student,String> tm = new TreeMap<>();

        Student s1 = new Student("zhangsan",23);
        Student s2 = new Student("lisi",22);
        Student s3 = new Student("wangwu",22);
        Student s4 = new Student("zhangsan",21);

        tm.put(s1,"江苏");
        tm.put(s2,"北京");
        tm.put(s3,"天津");
        tm.put(s4, "武汉");

        // 遍历TreeMap集合,打印每个学生的信息
        tm.forEach(
                (Student key, String value) -> {
                    System.out.println(key + "---" + value);
                }
        );
    }
}

四、可变参数

JDK1.5之后,如果我们定义一个方法需要接受多个参数,并且多个参数类型一致,我们可以对其简化.

  • 格式:
修饰符 返回值类型 方法名(参数类型... 形参名){  }
  • **底层:**其实就是一个数组

  • **好处:**在传递数据的时候,省的我们自己创建数组并添加元素了,JDK底层帮我们自动创建数组并添加元素了

  • 代码演示:

public class ChangeArgs {
    public static void main(String[] args) {
        int sum = getSum(8, 10, 5, 13, 2300);
        System.out.println(sum);
    }

    public static int getSum(int... arr) {
        int sum = 0;
        for (int a : arr) {
            sum += a;
        }
        return sum;
    }
}
  • 注意:

​ 1.一个方法只能有一个可变参数

​ 2.如果方法中有多个参数,可变参数要放到最后。

  • 应用场景: Collections

在Collections中也提供了添加一些元素方法:

public static <T> boolean addAll(Collection<T> c, T... elements) :往集合中添加一些元素。

public class CollectionsDemo {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        //原来写法
        list.add(9);
        list.add(14);
        list.add(8);
        list.add(320);
        System.out.println(list);  //[9, 14, 8, 320]

        //采用工具类 完成 往集合中添加元素
        Collections.addAll(list, 15, 14, 7, 310);
        System.out.println(list);

    }
}

五、Collections类

5.1 Collections常用功能

java.utils.Collections是集合工具类,用来对集合进行操作。常用方法如下:

  • public static void shuffle(List<?> list) :打乱集合顺序。
  • public static <T> void sort(List<T> list):将集合中元素按照默认规则排序。
  • public static <T> void sort(List<T> list,Comparator<? super T> ):将集合中元素按照指定规则排序。

代码演示:

public class CollectionsDemo1 {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        //原来写法
        list.add(9);
        list.add(8);
        list.add(320);
        list.add(14);
        System.out.println(list);  //[9, 8, 320, 14]

        //将集合中元素按照默认的自然规则排序
        Collections.sort(list);
        System.out.println(list);  //[8, 9, 14, 320]

        //打乱集合顺序
        Collections.shuffle(list);
        System.out.println(list);  //[320, 8, 14, 9]

    }
}

我们的集合按照默认的自然顺序进行了排列,如果想要指定顺序那该怎么办呢?

5.2 Comparator比较器

如果希望根据指定规则对集合进行排序,可使用public static <T> void sort(List<T> list,Comparator<? super T> )方法

Student类

public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

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

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

测试类

public class StudentSort {
    public static void main(String[] args) {
        List<Student> list = new ArrayList<>();
        list.add(new Student("Rose",18));
        list.add(new Student("Jack",16));
        list.add(new Student("LiHua",20));
        list.add(new Student("Jenny",18));

        Collections.sort(list, new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                //以学生的年龄升序,如果年龄一样 就按名字升序
                int result = o1.getAge() - o2.getAge();
                result = result==0 ? o1.getName().compareTo(o2.getName()) : result;
                return result;
            }
        });

        for (Student stu : list) {
            System.out.println(stu);
        }
    }
}

结果输出

Student{name=‘Jack’, age=16}
Student{name=‘Jenny’, age=18}
Student{name=‘Rose’, age=18}
Student{name=‘LiHua’, age=20}

六、综合练习

练习1:随机点名器

需求:班级里有N个学生,实现随机点名器

public class Test1 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        Collections.addAll(list,"范闲","范建","范统","杜子腾","杜琦燕","宋合泛","侯笼藤","朱益群","朱穆朗玛峰","袁明媛");

        //法一
        Random r = new Random();
        int index = r.nextInt(list.size());
        String name = list.get(index);
        System.out.println(name);

        //法二 打乱顺序
        Collections.shuffle(list);
        String name1 = list.get(0);
        System.out.println(name1);
    }
}

练习2:带概率的随机

需求:

​ 班级里有N个学生

​ 要求在随机的时候,70%的概率随机到男生,30%的概率随机到女生

public class Test2 {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        Collections.addAll(list,1,1,1,1,1,1,1);
        Collections.addAll(list,0,0,0);
        //打乱集合中的数据
        Collections.shuffle(list);
        //从list集合中随机抽取0或者1
        Random r = new Random();
        int index = r.nextInt(list.size());
        int number = list.get(index);
        System.out.println(number);

        //创建两个集合分别存储男生和女生的名字
        ArrayList<String> boyList = new ArrayList<>();
        ArrayList<String> girlList = new ArrayList<>();

        Collections.addAll(boyList,"范闲","范建","范统","杜子腾","宋合泛","侯笼藤","朱益群","朱穆朗玛峰");
        Collections.addAll(girlList,"杜琦燕","袁明媛","李猜","田蜜蜜");

        //判断此时是从boyList里面抽取还是从girlList里面抽取
        if(number == 1){
            //boyList
            int boyIndex = r.nextInt(boyList.size());
            String name = boyList.get(boyIndex);
            System.out.println(name);
        }else{
            //girlList
            int girlIndex = r.nextInt(girlList.size());
            String name = girlList.get(girlIndex);
            System.out.println(name);
        }
    }
}

练习3:随机不重复

需求:

​ 班级里有N个学生,被点到的学生不会再被点到。但是如果班级中所有的学生都点完了, 需要重新开启第二轮点名。

public class Test3 {
    public static void main(String[] args) {
        List<String> list1 = new ArrayList<>();
        Collections.addAll(list1, "范闲", "范建", "范统", "杜子腾", "杜琦燕", "宋合泛", "侯笼藤", "朱益群", "朱穆朗玛峰", "袁明媛");
        //创建一个临时的集合,用来存已经被点到学生的名字
        List<String> list2 = new ArrayList<>();
        
        //外循环:表示轮数
        for (int i = 0; i < 10; i++) {
            System.out.println("=========第" + i + "轮点名开始了======================");
            int count = list1.size();
            Random r = new Random();
            //内循环:每一轮中随机循环抽取的过程
            for (int j = 0; j < count; j++) {
                int index = r.nextInt(list1.size());
                String name = list1.remove(index);
                list2.add(name);
                System.out.println(name);
            }
            //此时表示一轮点名结束
            //list1 空了 list2 10个学生的名字
            list1.addAll(list2);
            list2.clear();
        }
    }
}

练习4:集合的嵌套

需求:

​ 定义一个Map集合,键用表示省份名称province,值表示市city,但是市会有多个。

添加完毕后,遍历结果格式如下:

江苏省 = 南京市,扬州市,苏州市,无锡市,常州市
湖北省 = 武汉市,孝感市,十堰市,宜昌市,鄂州市
河北省 = 石家庄市,唐山市,邢台市,保定市,张家口市

public class Test4 {
    public static void main(String[] args) {
        Map<String, ArrayList<String>> hm = new HashMap<>();
        //创建单列集合存储市
        ArrayList<String> city1 = new ArrayList<>();
        city1.add("南京市");
        city1.add("扬州市");
        city1.add("苏州市");
        city1.add("无锡市");
        city1.add("常州市");

        ArrayList<String> city2 = new ArrayList<>();
        city2.add("武汉市");
        city2.add("孝感市");
        city2.add("十堰市");
        city2.add("宜昌市");
        city2.add("鄂州市");

        ArrayList<String> city3 = new ArrayList<>();
        city3.add("石家庄市");
        city3.add("唐山市");
        city3.add("邢台市");
        city3.add("保定市");
        city3.add("张家口市");

        //把省份和多个市添加到map集合
        hm.put("江苏省",city1);
        hm.put("湖北省",city2);
        hm.put("河北省",city3);

        Set<Map.Entry<String, ArrayList<String>>> entries = hm.entrySet();
        for (Map.Entry<String, ArrayList<String>> entry : entries) {
            //entry依次表示每一个键值对对象
            String key = entry.getKey();
            ArrayList<String> value = entry.getValue();
            StringJoiner sj = new StringJoiner(", ","","");
            for (String city : value) {
                sj.add(city);
            }
            System.out.println(key + " = " + sj);
        }
    }
}

参考黑马程序员相关视频与笔记、【查漏补缺】Java 集合详解!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1719429.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

退出登录后选择记住登录状态回显用户名和密码

项目背景 : react ant 需求 : 退出登录后 , 选择了记住登录 , 回显用户名和密码 ; 未选择记住 , 则不回显用户名和密码 如图注意 : 发现一个鸡肋的问题 , 未勾选退出后 , 还是会回显 , 后来我查看了cookie和自己的逻辑都没问题 , 原来是因为我保存了密码 , 浏览器保存后自动渲…

C# 代码配置的艺术

文章目录 1、代码配置的定义及其在软件工程中的作用2、C# 代码配置的基本概念和工具3、代码配置的实践步骤4、实现代码配置使用属性&#xff08;Properties&#xff09;使用配置文件&#xff08;Config Files&#xff09;使用依赖注入&#xff08;Dependency Injection&#xf…

Echarts 让柱状图在图表中展示,离开X轴

文章目录 需求分析需求 分析 话不多说,直接源码展示 option = {title: {text: Waterfall Chart,subtext: Li

数据隐私重塑:Web3时代的隐私保护创新

随着数字化时代的不断深入&#xff0c;数据隐私保护已经成为了人们越来越关注的焦点之一。而在这个数字化时代的新篇章中&#xff0c;Web3技术作为下一代互联网的代表&#xff0c;正在为数据隐私保护带来全新的创新和可能性。本文将深入探讨数据隐私的重要性&#xff0c;Web3时…

数字孪生技术为何备受各行业青睐?

数字孪生技术近年来在各行业中受到越来越多的重视&#xff0c;这是因为它具备了显著的优势和广泛的应用前景。数字孪生是指利用数字化技术&#xff0c;在虚拟空间中创建一个与现实世界对应的虚拟模型&#xff0c;通过数据的实时交互和反馈&#xff0c;实现对物理实体的模拟和监…

嵌入式Linux复制剪切删除指令详解

指令操作 1. cp 复制指令 a. 用法&#xff1a;cp [ 选项 ] [ 源文件或目录 ] [ 目标文件或目录 ]&#xff1b; b. 用途&#xff1a;用于复制文件或目录&#xff1b; c. 通常情况下&#xff0c;复制的都不是空文件夹&#xff0c;所以直接使用 cp 复制空文件会失败&#xff0…

三体中的冯诺依曼

你叫冯诺依曼&#xff0c;是一位科学家。你无法形容眼前的现态&#xff0c;你不知道下一次自己葬身火海会是多久&#xff0c;你也不知道会不会下一秒就会被冰封&#xff0c;你唯一知道的&#xff0c;就是自己那寥寥无几的科学知识&#xff0c;你可能会抱着他们终身&#xff0c;…

全国产飞腾模块麒麟信安操作系统安全漏洞

1、背景介绍 目前在全国产飞腾模块上部署了麒麟信安操作系统&#xff0c;经第三方机构检测存在以下漏洞 操作系统版本为 内核版本为 openssh版本为 2、openssh CBC模式漏洞解决 首先查看ssh加密信息 nmap --script "ssh2*" 127.0.0.1 | grep -i cbc 可以通过修改/…

结构设计模式 - 代理设计模式 - JAVA

代理设计模式 一. 介绍二. 代码示例2.1 定义 CommandExecutor 类2.2 定义 CommandExecutorProxy代理类2.3 模拟客户端2.4 测试结果 三. 结论 前言 这是我在这个网站整理的笔记,有错误的地方请指出&#xff0c;关注我&#xff0c;接下来还会持续更新。 作者&#xff1a;神的孩子…

数据结构(三)循环链表 约瑟夫环

文章目录 一、循环链表&#xff08;一&#xff09;概念&#xff08;二&#xff09;示意图&#xff08;三&#xff09;操作1. 创建循环链表&#xff08;1&#xff09;函数声明&#xff08;2&#xff09;注意点&#xff08;3&#xff09;代码实现 2. 插入&#xff08;头插&#x…

【数据分享】中国劳动统计年鉴(1991-2023)

大家好&#xff01;今天我要向大家介绍一份重要的中国劳动统计数据资源——《中国劳动统计年鉴》。这份年鉴涵盖了从1991年到2023年中国劳动统计全面数据&#xff0c;并提供限时免费下载。&#xff08;无需分享朋友圈即可获取&#xff09; 数据介绍 1991年以来&#xff0c;中…

玄机平台应急响应—Linux日志分析

1、前言 啥是日志呢&#xff0c;日志就是字面意思&#xff0c;用来记录你干了啥事情。日志大体可以分为网站日志和系统日志&#xff0c;网站日志呢就是记录哪个用户在哪里什么时候干了啥事&#xff0c;以及其它的与网站相关的事情。系统日志呢&#xff0c;就是记录你的电脑系统…

飞腾+FPGA多U多串全国产工控主机

飞腾多U多串工控主机基于国产化飞腾高性能8核D2000处理器平台的国产自主可控解决方案&#xff0c;搭载国产化固件,支持UOS、银河麒麟等国产操作系统&#xff0c;满足金融系统安全运算需求&#xff0c;实现从硬件、操作系统到应用的完全国产、自主、可控&#xff0c;是国产金融信…

【计算机毕设】基于SpringBoot的房产销售系统设计与实现 - 源码免费(私信领取)

免费领取源码 &#xff5c; 项目完整可运行 &#xff5c; v&#xff1a;chengn7890 诚招源码校园代理&#xff01; 1. 研究目的 随着房地产市场的发展和互联网技术的进步&#xff0c;传统的房产销售模式逐渐向线上转移。设计并实现一个基于Spring Boot的房产销售系统&#xff0…

用HAL库改写江科大的stm32入门-6-3 PWM驱动LED呼吸灯

接线图&#xff1a; 2 :实验目的&#xff1a; 利用pwm实现呼吸灯。 关键PWM定时器设置&#xff1a; 代码部分&#xff1a; int main(void) {/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*…

神经网络---卷积神经网络CNN

一、从前馈神经网络到CNN 前馈神经网络&#xff08;Feedforward Neural Networks&#xff09;是最基础的神经网络模型&#xff0c;也被称为多层感知机&#xff08;MLP&#xff09;。 它由多个神经元组成&#xff0c;每个神经元与前一层的所有神经元相连&#xff0c;形成一个“…

【C语言】基于C语言实现的贪吃蛇游戏

【C语言】基于C语言实现的贪吃蛇游戏 &#x1f525;个人主页&#xff1a;大白的编程日记 &#x1f525;专栏&#xff1a;C语言学习之路 文章目录 【C语言】基于C语言实现的贪吃蛇游戏前言一.最终实现效果一.Win32 API介绍1.1Win32 API1.2控制台程序1.3控制台屏幕上的坐标COORD…

【kubernetes】探索k8s集群的存储卷、pvc和pv

目录 一、emptyDir存储卷 1.1 特点 1.2 用途 1.3部署 二、hostPath存储卷 2.1部署 2.1.1在 node01 节点上创建挂载目录 2.1.2在 node02 节点上创建挂载目录 2.1.3创建 Pod 资源 2.1.4访问测试 2.2 特点 2.3 用途 三、nfs共享存储卷 3.1特点 3.2用途 3.3部署 …

放开了去的 ulimit

放开了去的 ulimit 放开了去的 ulimitulimit简介临时修改打开文件数目永久修改系统总打开句柄限制更多信息 放开了去的 ulimit ulimit简介 对于高并发或者频繁读写文件的应用程序而言&#xff0c;有时可能需要修改系统能够打开的最多文件句柄数&#xff0c;否则就可能会出现t…

【网络原理】HTTP|认识请求“报头“|Host|Content-Length|Content-Type|UA|Referer|Cookie

目录 认识请求"报头"(header) Host Content-Length Content-Type User-Agent(简称UA) Referer &#x1f4a1;Cookie&#xff08;最重要的一个header&#xff0c;开发&面试高频问题&#xff09; 1.Cookie是啥&#xff1f; 2.Cookie怎么存的&#xff1f; …