资讯动态

Java集合框架入门:ArrayList、HashMap、HashSet用法详解与刷题避坑

发布时间:2026/10/22 6:21:03 来源:尧图企业网站定制
刷题刷到集合这一块很多零基础的同学会突然卡住。前面学数组的时候还觉得挺简单一碰到ArrayList、HashMap、HashSet这几个名字就开始发懵——它们到底什么时候用哪个方法怎么记牛客上的集合类题目看着基础但恰恰是区分“语法背熟了”和“真能写代码”的第一个分水岭。我准备用两期专题的时间手把手带刷集合类Collection和 Map把常用方法从参数到返回值全部捋一遍再配合典型题目走一遍完整思路保证零基础也能跟上。这个专题不打算绕弯子直接从刷题人的视角出发题目让你存什么、取什么、怎么遍历你就用对应的集合类和方法。先把集合框架的脉络理清楚再把 API 用熟最后用三道真题把代码落到 IDE 里跑通。你会发现集合类没那么玄它就是 Java 帮你封装好的“数据容器”你只要知道怎么用就行。1. 集合类与Map的体系脉络先搞清这张地图再动手很多新手学集合的第一反应是去背类名什么ArrayList、LinkedList、HashSet、HashMap背了半天还是混。我建议反过来先忘掉具体类只看两个顶层接口——Collection和Map。Java 里所有集合类归根到底不是实现了Collection就是实现了Map不存在第三种情况。把这条主线抓住后面的细节都好说。1.1 Collection家族List、Set、Queue的分工Collection是一个顶层接口它下面派生了三个子接口List、Set、Queue。这三个词记住了集合的分类就懂了一半。List是“有序、可重复”的列表。所谓有序就是元素有下标你放进去的顺序和取出来的顺序一致所谓可重复就是同一个对象可以放进去两次、三次都没问题。生活里类比就是排队打饭每个人按顺序站好就算两个人长得一样也各站各的位置。刷题时最常见的ArrayList就是 List 的代表实现它的底层是动态数组查下标快尾部增删也快。LinkedList底层是双向链表中间插入删除有优势但平时刷题用得少备着认识就行。Set是“不重复”的集合。它没有下标概念也不保证和插入顺序一致LinkedHashSet除外。它存在的核心意义就是去重你往里面丢重复元素它只会保留一份。类比就是打卡签到一个人只能签一次。HashSet是 Set 的默认选择底层依赖哈希表TreeSet底层是红黑树放进去自动排序需要有序去重时可以换它。Queue是队列接口FIFO先进先出结构刷题涉及Deque双端队列时用得比较多比如用ArrayDeque模拟栈或者队列。零基础阶段先把List和Set吃透Queue遇到具体题目再说也不迟。1.2 Map不继承Collection的独立体系Map和Collection完全是两条线。Map存的是“键值对”也就是一个 key 对应一个 value通过 key 直接找 value。类比就是手机通讯录联系人姓名是 key电话号码是 value你按姓名查号码而不是按顺序一个个翻。HashMap最常用底层数组加链表查找效率高但无序。LinkedHashMap保持插入顺序常用于需要按顺序遍历的场景。TreeMap按键排序底层红黑树。Hashtable老古董线程安全但性能差刷题基本不用。有人会问Map能算集合吗从语法上看它不在Collection体系里但它属于集合框架的一等公民。刷题时看到“统计每个字母出现次数”、“按单词分组”这类需求别犹豫上HashMap就对了。1.3 刷题时怎么选集合先看场景再选工具刷题和面试最大的区别在于题目不会告诉你要用哪个类你得自己判断。我给自己定了一个简单的选择流程要存一串有顺序、允许重复的数据ArrayList要存不重复的数据、并且不在乎顺序HashSet要用一个 key 去快速找 valueHashMap要去重之后再排序TreeSet要按键排序遍历TreeMap这个流程看着简单但做题时真管用。很多同学纠结半天选错集合最后不是代码写错而是数据结构选错了。记住一句话先确定需求再选容器最后才写方法。容器选对了代码就成功了一半。2. 方法详解这些API就是刷题时的基础招式选好了集合类下一步就是把该类的常用方法记熟。刷题不要求你把所有方法都背下来但高频操作就那么几个增、删、查、遍历、判空、大小。我把List和Map的高频方法分开整理每个方法说清楚作用、参数、返回值顺带补一句实战经验。2.1 Collection接口的常用方法清单Collection 的子类List 和 Set共有一批基础方法这些方法全是从接口继承下来的方法作用参数返回值备注add(E e)添加元素要添加的元素boolean一般返回 trueremove(Object o)删除指定元素要删除的元素boolean删除成功返回 truecontains(Object o)是否包含某元素对象boolean底层依赖 equalssize()集合元素个数无int别和数组 length 混isEmpty()是否为空无boolean判断空优先用它clear()清空元素无void只是清空引用iterator()获取迭代器无Iterator遍历专用这里有一个零基础容易踩的误区Collection没有get()方法。能通过下标取元素的只有List因为List才有 index 概念Set无序所以你无法set.get(0)。刷题时如果你想遍历Set只能用增强 for 或者迭代器。2.2 Map接口的常用方法清单Map 的方法和 Collection 完全不同核心是围绕 key 操作 value方法作用参数返回值备注put(K key, V value)放入键值对key 和 valueV返回旧 value没有则 nullget(Object key)根据 key 取 valuekeyVkey 不存在返回 nullremove(Object key)删除该 key 的键值对keyV返回被删的 valuecontainsKey(Object key)是否包含某个 keykeyboolean判断 key 是否存在containsValue(Object value)是否包含某个 valuevalueboolean用得少效率低keySet()返回所有 key 的集合无Set遍历 key 用values()返回所有 value 的集合无Collection遍历 value 用entrySet()返回所有键值对无Set同时拿 key 和 valuegetOrDefault(Object key, V defaultValue)key 不存在时返回默认值key 和默认值V统计场景很好用我要特别强调两个方法keySet()和entrySet()。刷题时最常见的遍历写法有两种一种是先拿keySet()再通过 key 去 get value每次都要查一次表另一种是直接遍历entrySet()一次把 key 和 value 都拿出来。如果只是要 key用 keySet如果 key 和 value 都要直接用 entrySet少一次查询思路也更直观。2.3 三种遍历方式的选择零基础阶段见的遍历方式有三种普通 for 循环、增强 for、迭代器。普通 for 循环只适用于List因为只有它有下标。增强 for 适用于所有 Collection 和 Map 的视图比如 keySet、entrySet代码最简洁。迭代器写法较繁琐但有一个不可替代的优势可以在遍历过程中安全地删除元素。这个坑我后面会细讲。初学刷题时我推荐大部分场景直接用增强 for简洁不容易出错。但“遍历时删元素”的场景就要换成迭代器这是考试和面试里高频出现的考点。3. 手把手实战三道典型题把集合类的用法落到代码里方法背得再熟不写代码等于白学。这一节我直接带刷三道牛客风格的典型题题目都不难但能覆盖前面提到的绝大部分 API。每道题我都给完整代码和运行输出突出关键方法的位置和意图。强烈建议你边看边敲不要只读。3.1 题目一用ArrayList存储并遍历多个元素题目要求创建一个 ArrayList依次添加三门课程的名字然后输出课程总数再通过下标遍历输出每一门课程。这种题就是让新手熟悉add()、size()、get()这三个最基础的方法。下面是我在 IDE 里跑通的完整代码import java.util.ArrayList; import java.util.List; public class Main { public static void main(String[] args) { ListString courses new ArrayList(); courses.add(JavaSE); courses.add(数据结构); courses.add(操作系统); System.out.println(课程总数 courses.size()); for (int i 0; i courses.size(); i) { System.out.println(第 (i 1) 门课 courses.get(i)); } } }运行输出课程总数3 第1门课JavaSE 第2门课数据结构 第3门课操作系统注意两点第一new ArrayList()左边的类型写ListString这是一个好习惯面向接口编程第二size()是方法后面要加括号数组的length是属性不用加括号两个千万别混。如果你想把这三门课全部清理掉就调用courses.clear()再打印courses.size()就是 0。这种清空操作在题目要求多次复用集合时很有用。3.2 题目二用HashMap统计字符串中每个字符出现的次数题目要求给定一个字符串hello world统计每个字符包括空格出现的次数并输出结果。这道题是 HashMap 最经典的场景——统计频率。核心思路是遍历字符串的每一个字符如果 map 里没有该字符就放入次数 1如果有就在原次数上加 1。用getOrDefault可以让代码非常简洁import java.util.HashMap; import java.util.Map; public class Main { public static void main(String[] args) { String str hello world; MapCharacter, Integer countMap new HashMap(); for (int i 0; i str.length(); i) { char c str.charAt(i); countMap.put(c, countMap.getOrDefault(c, 0) 1); } for (Map.EntryCharacter, Integer entry : countMap.entrySet()) { System.out.println(entry.getKey() - entry.getValue()); } } }运行输出- 1 d - 1 e - 1 h - 1 l - 3 o - 2 r - 1 w - 1getOrDefault(c, 0) 1这一行是整道题的精髓如果字符没统计过就当成 0 然后加 1统计过就取出旧值加 1。不用先判断 containsKey也不用写 if-else一行搞定。有人会问输出结果里空格显示为空看着有点怪。这是因为entrySet()遍历时空格字符打印出来就是空白。如果你不想统计空格可以在循环里加一个判断if (c ) continue;直接跳过。这种小细节在牛客的字符串题目里经常出现。3.3 题目三用HashSet对整数列表去重题目要求给定一组整数其中包含重复值请去除重复元素并输出去重后的个数和元素。去重这个需求用 HashSet 是最直观的解。HashSet 的 add 方法天然具有去重能力你只需要把列表里的元素一个个丢进去import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; public class Main { public static void main(String[] args) { ListInteger numbers new ArrayList(); numbers.add(5); numbers.add(3); numbers.add(5); numbers.add(1); numbers.add(3); SetInteger unique new HashSet(numbers); System.out.println(去重后的个数 unique.size()); for (Integer num : unique) { System.out.print(num ); } } }运行输出去重后的个数3 1 3 5这里有一个很多人问的问题直接把numbers传给HashSet的构造函数这算不算一种遍历算的Java 的 Collection 构造函数天然支持把另一个集合传进去底层会自动遍历添加。这比手动写 for 循环更简洁。另外你可能会发现输出顺序是1 3 5而不是原来的5 3 1。这很正常HashSet本身无序输出顺序取决于哈希结果。如果你需要保留原顺序去重可以把HashSet换成LinkedHashSet如果需要去重后再排序就换成TreeSet。这三种 Set 的差异背下来不如亲手跑一次来得深刻。4. 刷题避坑实录这些错误比语法错误更隐蔽语法错误 IDE 会给你标红但逻辑错误不会。集合这一块有几个经典坑几乎每个零基础同学都会踩一遍。我把它们集中整理出来每个都附上现象、原因和解决方案帮你省下几个小时的排查时间。4.1 迭代时删除元素ConcurrentModificationException的由来很多同学第一次遇到这个异常是在写“删除列表里的偶数”这类题目时。写出来的代码长这样ListInteger list new ArrayList(); list.add(1); list.add(2); list.add(3); list.add(4); for (Integer num : list) { if (num % 2 0) { list.remove(num); // 运行时报错 } }一运行就抛ConcurrentModificationException。原因很简单增强 for 底层用的是迭代器迭代器会维护一个modCount修改次数的期望值。你在迭代过程中直接调用list.remove()集合的修改次数变了迭代器发现不一致立刻抛异常防止出现不可预知的遍历结果。正确的做法是使用迭代器的remove()方法或者先记录要删除的元素循环结束后统一删除IteratorInteger iterator list.iterator(); while (iterator.hasNext()) { Integer num iterator.next(); if (num % 2 0) { iterator.remove(); } }4.2 HashSet和HashMap依赖equals与hashCode第二个隐蔽的坑和自定义对象有关。假设你用HashSet存一个自定义的Student对象两个学生虽然学号相同却被当成两个元素存了进去class Student { String name; String id; // 没有重写 equals 和 hashCode }HashSet去重的本质是“先比 hashCode相同再比 equals”。如果你没有重写这两个方法默认比较的是对象地址两个 new 出来的对象地址不同自然就不算重复。刷题时常见的是读入数据后创建对象没注意重写equals结果去重失败。解决方案没有捷径自定义类如果需要参与去重或排序必须重写equals()和hashCode()。注意规则equals 相等的对象hashCode 必须相等hashCode 相等时equals 可以不等哈希碰撞。看不懂没关系记住先重写这两个方法后面学哈希表原理时自然就通了。4.3 Arrays.asList的两个隐藏陷阱Arrays.asList(1, 2, 3)这个 API 在刷题时很常用但用不好就是两个大坑。第一它返回的List的长度是固定的不能add()也不能remove()一调就抛UnsupportedOperationException。原因在于底层是数组实现的固定大小列表。如果你想得到一个真正可以增删的列表需要再包一层new ArrayList(Arrays.asList(...))。第二asList对基本类型数组的处理很坑。如果你写Arrays.asList(new int[]{1, 2, 3})得到的不是三个元素的列表而是一个只有一个元素且元素类型是int[]的列表。因为泛型不能接收基本类型int[]被当成了一个整体对象。解决方案是用包装类数组Integer[] arr {1, 2, 3}; Arrays.asList(arr);。4.4 常见问题速查表我把集合刷题时高频出现的问题整理成一张速查表方便你遇到报错时对照排查错误现象可能原因排查方向ConcurrentModificationException增强 for 中直接调用集合的 remove改用迭代器的 remove 或先记录再删除UnsupportedOperationException对Arrays.asList结果调 add/remove外层再包new ArrayList()HashSet 去重失败自定义类没重写 equals/hashCode给类补上两个方法get()返回 nullkey 不存在或 value 本身是 null用 containsKey 判断再取值遍历顺序和插入顺序不一致用了 HashMap / HashSet需要有序时换 LinkedHashMap / LinkedHashSetArrayList扩容报 OOM一次性添加海量数据预估容量构造函数传入初始大小最后再分享一个我个人的刷题心得集合相关的代码每写完一个操作顺手打印一行关键变量看结果。不要觉得打印麻烦零基础阶段调试信息是你最好的老师。跑一次改一处比闷头背十遍方法签名都管用。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑