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

文章目录

  • 一、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 集合详解!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

另一棵树的子树(oj题)

一、题目链接 https://leetcode.cn/problems/subtree-of-another-tree/submissions/536304222 二、题目思路 1.首先遍历大树&#xff0c;判断大树的根结点的值是否等于小树的根结点的值&#xff0c;如果不相等&#xff0c;就找大树的左孩子或者右孩子&#xff0c;以左孩子为根…

博士毕业论文/CTEX/LATEX

LATEX环境安装 CTEX 安装 &#xff08;垃圾&#xff0c;不要装&#xff09; 运行 clean.batcomp.bat 缺少字体 Couldn’t find Adobe Heiti S.cfg’ miktex-maketfm: No creation rule for font “Adobe Heiti Std”.解决方法&#xff1a;其实就是下载这四个字体之后&…

jsp实验19 File

三、源代码以及执行结果截图&#xff1a; readJSPFile.jsp <% page contentType"text/html" %> <% page pageEncoding "utf-8" %> <% page import"java.io.*"%> <style> #tom{ font-family:宋体;font-size:2…

Crosslink-NX器件应用连载(10): 图像输入并通过HDMI输出

作者&#xff1a;Hello,Panda 大家下午好&#xff0c;晚上好。这里分享一个Lattice Crosslink-NX器件通过MIPI或LVDS输入图像&#xff0c;并通过HDMI输出图像的案例&#xff08;其实这是个比较冷门的需求&#xff0c;Crosslink-NX器件还是主要做MIPI桥接用&#xff09;。 咱们…

《Kubernetes部署篇:基于麒麟V10+ARM64架构部署harbor v2.4.0镜像仓库》

总结&#xff1a;整理不易&#xff0c;如果对你有帮助&#xff0c;可否点赞关注一下&#xff1f; 更多详细内容请参考&#xff1a;企业级K8s集群运维实战 一、环境信息 K8S版本 操作系统 CPU架构 服务版本 1.26.15 Kylin Linux Advanced Server V10 ARM64 harbor v2.4.0 二、部…

文刻创作AI工具怎么注册卡密

在科技日新月异的今天&#xff0c;生成式人工智能&#xff08;AI&#xff09;的迅猛发展正在重塑各行各业的面貌。其中&#xff0c;文刻创作AI工具以其独特的魅力和强大的功能&#xff0c;成为了内容创作领域的一股清流&#xff0c;引领着内容生产的新潮流。 【文刻二创工具教…

人脸识别模型与人类视觉识别的对比——评估人脸识别模型存在偏见是否比人类的偏见大?

1. 概述 人脸识别系统是一个几十年来一直备受关注的研究领域。而且在过去的几年中。公司和政府一直在积极引入人脸识别系统&#xff0c;并且我们看到越来越多的机会可以看到人脸识别系统。例如&#xff0c;有的系统可以随便介绍&#xff0c;如搜索特定人的图像&#xff08;图像…

SAP Build引言

前言 SAP Build 似乎是一个整合了很多低代码或无代码产品的平台&#xff0c;最早的时候应该都是各自分开的几个产品&#xff0c;近年合并到一块上了SAP Build平台 现在看官网的介绍应该是有三四个产品被集成进来了&#xff0c;分别是SAP IRPA&#xff0c;SAP Workflow&#xf…

K8s Ingress 详解

文章目录 K8s Ingress 详解Ingress 资源清单Ingress 基于URL 实现路由Ingress 基于名称虚拟主机Ingress 实现HTTPS创建TLS 证书创建Secrets配置ingress Ingress RewriteIngress 灰度发布Ingress 配置认证 K8s Ingress 详解 Ingress 资源清单 apiVersion: networking.k8s.io/v…

编程学习 (C规划) 6 {24_4_18} 七 ( 简单扫雷游戏)

首先我们要清楚扫雷大概是如何实现的&#xff1a; 1.布置雷 2.扫雷&#xff08;排查雷&#xff09; &#xff08;1&#xff09;如果这个位置是雷就炸了&#xff0c;游戏结束 &#xff08;2&#xff09;如果不是雷&#xff0c;就告诉周围有几个雷 3.把所有不是雷的位置都找…

【Unity脚本】使用脚本操作游戏对象的组件

【知识链】Unity -> Unity脚本 -> 游戏对象 -> 组件 【知识链】Unity -> Unity界面 -> Inspector【摘要】本文介绍如何使用脚本添加、删除组件&#xff0c;以及如何访问组件 文章目录 引言第一章 游戏对象与组件1.1什么是组件&#xff1f;1.2 场景、游戏对象与组…

C++11中的新特性(2)

C11 1 可变参数模板2 emplace_back函数3 lambda表达式3.1 捕捉列表的作用3.2 lambda表达式底层原理 4 包装器5 bind函数的使用 1 可变参数模板 在C11之前&#xff0c;模板利用class关键字定义了几个参数&#xff0c;那么我们在编译推演中&#xff0c;我们就必须传入对应的参数…

Mac安装第三方软件的命令安装方式

场景&#xff1a; 打开终端命令行&#xff0c;sudo xattr -rd com.apple.quarantine&#xff0c;注意最后quarantine 后面加一个空格&#xff01;然后打开Finder&#xff08;访达&#xff09;&#xff0c;点击左侧的 应用程序&#xff0c;找到相关应用&#xff0c;拖进终端qua…

指纹浏览器大全

具体请前往&#xff1a;国内外指纹浏览器大全

vue3组件通信与props

title: vue3组件通信与props date: 2024/5/31 下午9:00:57 updated: 2024/5/31 下午9:00:57 categories: 前端开发 tags: Vue3组件Props详解生命周期数据通信模板语法Composition API单向数据流 Vue 3 组件基础 在 Vue 3 中&#xff0c;组件是构建用户界面的基本单位&#…

V90 PN总线伺服通过FB285速度控制实现正弦位置轨迹运动(解析法和数值法对比测试)

V90总线伺服相关内容请参考专栏系列文章,这里不在详述 1、V90伺服PN总线速度随动控制 V90伺服PN总线速度随动控制(手摇轮功能)_手摇轮可以接总线plc吗?-CSDN博客文章浏览阅读632次。V90PN总线控制相关内容,请参考下面文章链接:博途1200/1500PLC V90 PN通信控制 (FB284功能…

构建企业级AI私有知识库

一、引言 在当今竞争激烈的市场环境中&#xff0c;企业为了保持竞争优势&#xff0c;需要高效地管理和利用内部知识资源。构建一个企业级AI私有知识库&#xff0c;不仅可以集中存储和管理企业知识&#xff0c;还能通过人工智能技术实现知识的智能化处理和利用。本文将详细介绍…

模型 STORY评估框架

说明&#xff1a;系列文章 分享 模型&#xff0c;了解更多&#x1f449; 模型_思维模型目录。故事五要素&#xff1a;结构、时间、观点、现实、收益 。 1 STORY评估框架的应用 1.1 STORY模型展示其个性化在线学习解决方案的优势 一家在线教育平台想要通过一个故事来展示其个性…

Spring 框架:Java 企业级开发的基石

文章目录 序言Spring 框架的核心概念Spring 框架的主要模块Spring Boot&#xff1a;简化 Spring 开发Spring Cloud&#xff1a;构建微服务架构实际案例分析结论 序言 Spring 框架自 2002 年发布以来&#xff0c;已经成为 Java 企业级开发的标准之一。它通过提供全面的基础设施…

Qt 窗口

在Qt Creator 中创建项目的时候&#xff0c;我们能够选择创建QMainWindow 还是 QWidget 两种窗口。 二者有什么区别呢&#xff1f;其中 QMainWindow 是一种主窗口&#xff0c;包含菜单栏&#xff0c;工具栏&#xff0c;状态栏&#xff0c;中心窗口和浮动窗口等多个窗口组合&…