Collection、Map 与常用实现类详解(附思维导图)

发布时间:2026/6/10 15:05:28

Collection、Map 与常用实现类详解(附思维导图) 本文重点讲解Collection单列集合、Map双列集合、常见实现类、Map 遍历方式以及自定义对象作为 Map 键时为什么必须重写hashCode()和equals()。一、Java 集合框架整体结构Java 集合框架主要分为两大体系体系说明常见接口 / 实现类Collection单列集合一次存储一个元素List、SetMap双列集合一次存储一组键值对HashMap、LinkedHashMap、TreeMap、Hashtable其中Collection更适合存储一批单独的数据例如学生对象、商品对象、订单对象。Map更适合存储有对应关系的数据例如用户 ID 对应用户对象、商品编号对应商品详情、配置名对应配置值。集合框架总览思维导图从图中可以看出List有序、可重复、有索引。Set无序、唯一、无索引。Map键值对结构Key唯一Value可以重复。二、Collection 单列集合Collection是单列集合的顶层接口下面主要有两个常用子接口ListSet三、List 集合List的特点有序存入和取出的顺序一致。可重复可以存储重复元素。有索引可以通过下标访问元素。常用实现类实现类底层结构特点ArrayList数组查询快增删慢非线程安全LinkedList双向链表查询慢增删快非线程安全1. ArrayListArrayList底层是数组。特点根据索引查询元素快。中间位置增删元素时需要移动元素所以增删相对慢。线程不安全适合单线程场景。示例ListString list new ArrayList(); ​ list.add(Java); list.add(MySQL); list.add(Redis); ​ System.out.println(list.get(0)); // Java2. LinkedListLinkedList底层是双向链表。特点查询元素时需要从头或尾开始找查询相对慢。增删元素时只需要修改节点指向增删相对快。可以作为队列或栈使用。示例LinkedListString list new LinkedList(); ​ list.add(Java); list.addFirst(MySQL); list.addLast(Redis); ​ System.out.println(list);四、Set 集合Set的特点无序存入和取出的顺序不一定一致。唯一不能存储重复元素。无索引不能通过下标访问元素。常用实现类实现类底层结构特点HashSet哈希表去重、无序、查询快LinkedHashSet哈希表 链表去重、有序TreeSet红黑树去重、可排序1. HashSetHashSet底层依赖哈希表。特点元素唯一。查询效率高。不保证存取顺序。SetString set new HashSet(); set.add(Java); set.add(Java); set.add(MySQL); System.out.println(set); // Java 只会保留一份2. LinkedHashSetLinkedHashSet底层是哈希表 链表。特点元素唯一。保证存取顺序。查询效率较高。SetString set new LinkedHashSet(); set.add(Java); set.add(MySQL); set.add(Redis); System.out.println(set); // 按添加顺序输出3. TreeSetTreeSet底层是红黑树。特点元素唯一。可以对元素进行排序。存储的元素需要具备比较规则。排序规则有两种写法元素类实现Comparable接口。创建集合时传入Comparator比较器。SetInteger set new TreeSet(); set.add(30); set.add(10); set.add(20); System.out.println(set); // [10, 20, 30]五、Map 双列集合Map是双列集合用来存储键值对。基本格式MapK, V含义KKey键。VValue值。例如MapInteger, String map new HashMap(); map.put(1, 张三); map.put(2, 李四); map.put(3, 王五);可以理解为1 - 张三 2 - 李四 3 - 王五Map 集合特点Map是双列集合。Key唯一不能重复。Value可以重复。一个Key只能对应一个Value。如果添加相同的Key后添加的Value会覆盖原来的Value。Map集合本身依赖键来管理数据。六、Map 常见实现类Map 体系思维导图从图中可以看出Map 常见实现类主要包括HashMapLinkedHashMapTreeMapHashtableProperties七、HashMapHashMap是最常用的 Map 实现类。1. 底层结构在 JDK 8 以后HashMap底层结构可以理解为数组 链表 红黑树当哈希冲突较少时主要使用数组和链表。当同一个桶中的链表长度较长并且数组容量达到一定条件后链表会转换为红黑树以提高查询效率。2. HashMap 特点特点说明是否有序无序是否允许 null允许一个null键允许多个null值是否线程安全不安全查询效率较高Key 是否唯一唯一是否需要重写方法自定义对象作为 Key 时需要重写hashCode()和equals()示例MapString, Integer map new HashMap(); ​ map.put(Java, 1); map.put(MySQL, 2); map.put(Redis, 3); ​ System.out.println(map.get(Java)); // 1八、LinkedHashMapLinkedHashMap是HashMap的子类。1. 底层结构哈希表 双向链表哈希表负责提高查询效率双向链表负责维护元素的存取顺序。2. LinkedHashMap 特点特点说明是否有序有序是否允许 null允许null键和null值是否线程安全不安全查询效率较高使用场景既需要键值对存储又需要保证遍历顺序示例MapString, String map new LinkedHashMap(); map.put(A, Java); map.put(B, MySQL); map.put(C, Redis); System.out.println(map);输出顺序通常与添加顺序一致。九、TreeMapTreeMap底层是红黑树。1. TreeMap 特点特点说明是否有序按 Key 排序底层结构红黑树是否允许 null 键一般不允许null键是否线程安全不安全使用场景需要按照 Key 排序的键值对数据示例MapInteger, String map new TreeMap(); map.put(3, 王五); map.put(1, 张三); map.put(2, 李四); System.out.println(map); // {1张三, 2李四, 3王五}2. TreeMap 排序方式TreeMap排序规则有两种方式一Key 实现 Comparable 接口class Student implements ComparableStudent { private String name; private int age; Override public int compareTo(Student o) { return this.age - o.age; } }方式二创建 TreeMap 时传入 ComparatorMapStudent, String map new TreeMap((s1, s2) - s1.getAge() - s2.getAge());十、HashtableHashtable是较早期的 Map 实现类。特点特点说明底层结构哈希表是否线程安全安全是否允许 null不允许null键和null值效率相对较低当前使用频率较少示例MapString, String map new Hashtable(); map.put(name, 张三); map.put(age, 18);实际开发中如果需要线程安全的 Map更常见的是使用ConcurrentHashMap而不是Hashtable。十一、Properties 集合Properties是Hashtable的子类通常用于读取配置文件。1. 特点Properties属于 Map 体系。主要用于处理属性配置。键和值一般都是字符串。常用于读取.properties文件。例如配置文件usernameroot password123456 drivercom.mysql.cj.jdbc.Driver2. 常用方法方法说明setProperty(String key, String value)设置属性getProperty(String key)根据 key 获取 valuestringPropertyNames()获取所有 key 的集合示例Properties properties new Properties(); properties.setProperty(username, root); properties.setProperty(password, 123456); String username properties.getProperty(username); System.out.println(username);十二、Map 常用方法总结方法作用V put(K key, V value)添加键值对如果 key 已存在会覆盖原来的 valueV get(Object key)根据 key 获取 valueV remove(Object key)根据 key 删除键值对void clear()清空集合boolean containsKey(Object key)判断是否包含某个 keyboolean containsValue(Object value)判断是否包含某个 valueint size()获取键值对数量boolean isEmpty()判断集合是否为空SetK keySet()获取所有 keyCollectionV values()获取所有 valueSetMap.EntryK, V entrySet()获取所有键值对对象十三、Map 的遍历方式Map 集合常见遍历方式主要有两种通过keySet()遍历。通过entrySet()遍历。十四、方式一keySet 遍历思路先获取所有的 key再根据 key 获取 value。MapString, String map new HashMap(); map.put(name, 张三); map.put(age, 18); map.put(address, 武汉); SetString keys map.keySet(); for (String key : keys) { String value map.get(key); System.out.println(key value); }理解map.keySet()表示获取 Map 中所有 key 组成的 Set 集合。然后通过map.get(key)根据 key 获取对应的 value。十五、方式二entrySet 遍历1. Map.Entry 是什么Map接口内部有一个内部接口Map.EntryK, V可以理解为Map.Entry 表示一个键值对对象每一个Entry对象中都包含一个 key一个 valueEntry内部常用方法方法说明getKey()获取 keygetValue()获取 valuesetValue(V value)修改当前 Entry 对象中的 value2. entrySet 遍历思想Map 集合通过键值对对象遍历集合的核心思想Map接口内部有Entry接口。Entry接口内部有getKey()、getValue()、setValue()方法。通过map.entrySet()获取所有键值对对象组成的集合。通过增强 for 循环遍历每一个键值对对象。因为每个对象都是Map.Entry类型所以可以调用getKey()和getValue()获取键和值。代码示例MapString, String map new HashMap(); map.put(name, 张三); map.put(age, 18); map.put(address, 武汉); SetMap.EntryString, String entries map.entrySet(); for (Map.EntryString, String entry : entries) { String key entry.getKey(); String value entry.getValue(); System.out.println(key value); }3. 重点理解map.entrySet()表示拿到所有键值对对象组成的集合Map.EntryString, String表示其中一个键值对对象entry.getKey()表示拿 keyentry.getValue()表示拿 value十六、entrySet 遍历为什么更常用对于 Map 来说entrySet()通常更推荐。原因keySet()遍历时需要先拿 key再通过map.get(key)查询 value。entrySet()遍历时直接拿到键值对对象可以直接获取 key 和 value。当数据量较大时entrySet()遍历通常更直接。对比// keySet 方式 for (String key : map.keySet()) { String value map.get(key); } // entrySet 方式 for (Map.EntryString, String entry : map.entrySet()) { String key entry.getKey(); String value entry.getValue(); }十七、自定义对象作为 Map 的 Key如果 Map 的 Key 是自定义对象一般必须重写hashCode() equals()例如MapStudent, String map new HashMap();这里Student是自定义类如果不重写hashCode()和equals()Map 就无法正确判断两个 Student 对象是否应该被认为是同一个 key。十八、为什么要同时重写 hashCode 和 equals1. HashMap 判断 Key 是否重复的基本流程HashMap 存储数据时大致流程如下先根据 key 的 hashCode() 计算哈希值 再根据哈希值定位到数组中的桶位置 如果桶中已有元素再用 equals() 判断 key 是否相同 如果 key 相同覆盖 value 如果 key 不同新增键值对因此hashCode()负责确定大概位置。equals()负责判断是否真正相等。2. 只重写 hashCode不重写 equals如果只重写hashCode()不重写equals()逻辑相同的对象可能会被分配到同一个哈希桶中 但是默认的 equals() 比较的是对象地址 Map 无法识别它们是同一个 key。结果本来应该覆盖的 key 变成了重复存储。破坏了 Map 中 key 唯一的逻辑。查询时可能得不到预期结果。错误示例class Student { private String name; private int age; ​ Override public int hashCode() { return Objects.hash(name, age); } ​ // 没有重写 equals() }3. 只重写 equals不重写 hashCode如果只重写equals()不重写hashCode()逻辑相同的对象虽然 equals() 判断为 true 但由于 hashCode() 仍然使用默认规则 不同对象可能计算出不同的哈希值 最终定位到不同的桶中。结果equals()可能根本没有机会执行。明明逻辑上是同一个 key却可能被存入不同位置。查询时可能出现“明明存了数据却取不出来”的问题。还可能出现大量重复的无效 key占用额外内存。错误示例class Student { private String name; private int 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); } ​ // 没有重写 hashCode() }4. 正确写法同时重写 hashCode 和 equalsimport java.util.Objects; ​ public class Student { private String name; private int age; ​ public Student(String name, int age) { this.name name; 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); } }测试MapStudent, String map new HashMap(); Student s1 new Student(张三, 18); Student s2 new Student(张三, 18); map.put(s1, 第一次添加); map.put(s2, 第二次添加); System.out.println(map.size()); // 1因为s1和s2的姓名、年龄相同所以会被认为是同一个 key第二次添加会覆盖第一次添加的 value。十九、包装类作为 Key 为什么不用自己重写如果 Map 的 Key 是 Java 已经提供好的类型一般不需要自己重写hashCode()和equals()。例如MapInteger, String map1 new HashMap(); MapString, String map2 new HashMap(); MapCharacter, String map3 new HashMap();原因是IntegerStringCharacterLongDouble这些类内部已经重写好了hashCode()和equals()。所以可以直接作为 Map 的 Key 使用。二十、HashMap、LinkedHashMap、TreeMap、Hashtable 对比集合底层结构是否有序是否允许 null是否线程安全主要特点HashMap数组 链表 红黑树无序允许一个 null 键多个 null 值不安全最常用效率高LinkedHashMap哈希表 双向链表有序允许 null 键和值不安全保证存取顺序TreeMap红黑树按 Key 排序一般不允许 null 键不安全Key 可排序Hashtable哈希表无序不允许 null 键和值安全老旧类效率较低PropertiesHashtable 子类无序键和值通常是字符串安全常用于配置文件二十一、如何选择集合1. 选择 List如果你需要元素可以重复。元素需要有序。需要通过索引访问。优先考虑ArrayList如果频繁在头部或中间增删可以考虑LinkedList2. 选择 Set如果你需要元素不能重复。可以选择场景推荐只需要去重不关心顺序HashSet需要去重并保证添加顺序LinkedHashSet需要去重并进行排序TreeSet3. 选择 Map如果你需要根据 key 快速查 value。存储一组一组的对应关系。可以选择场景推荐最常用的键值对存储HashMap需要按照添加顺序遍历LinkedHashMap需要按照 key 排序TreeMap读取配置文件Properties二十二、学习重点总结1. Collection 重点Collection 是单列集合 List有序、可重复、有索引 Set无序、唯一、无索引2. Map 重点Map 是双列集合 Key 唯一 Value 可以重复 一个 Key 对应一个 Value3. HashMap 重点HashMap 底层是数组 链表 红黑树 HashMap 无序 HashMap 允许 null 键和 null 值 HashMap 线程不安全 自定义对象作为 Key 时必须重写 hashCode 和 equals4. Map.Entry 重点map.entrySet()拿到所有键值对对象组成的集合 Map.Entry表示其中一个键值对对象 entry.getKey()获取 key entry.getValue()获取 value5. hashCode 和 equals 重点hashCode 决定对象存储的大概位置 equals 判断两个对象是否真正相等 自定义对象作为 HashMap 的 key 时两者必须同时重写二十三、结尾Java 集合框架的核心不是单纯背 API而是理解每种集合的底层结构和使用场景。可以按照下面的思路记忆要存一批单独的数据用 Collection 要存键值对应关系用 Map ​ 要有序、可重复用 List 要唯一、去重用 Set 要通过 key 找 value用 Map ​ 要查询快HashMap / HashSet 要保持顺序LinkedHashMap / LinkedHashSet 要排序TreeMap / TreeSet

相关新闻