- List :元素有序、允许重复、带数字索引。实现类:
ArrayList、LinkedList、Vector - Set :元素无序、不允许重复 、没有索引。实现类:
HashSet - Map :存储键值对
key=value;key 唯一不能重复;value 可重复。HashMap
遍历集合三种方式
scss
//1.普通for循环:只适用于List(有索引)
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
//2.增强for‑each(全部Collection都能用)
for(String s : list){
System.out.println(s);
}
//3.Iterator迭代器(Set必须用这个,删除元素安全)
Iterator<String> it = set.iterator();
while(it.hasNext()){
String s = it.next();
System.out.println(s);
}
ArrayList
- 底层:动态 Object 数组
- 查询快(数组随机索引);中间增删慢,需要数组拷贝移位
- 线程不安全;扩容:默认容量 10,扩容 1.5 倍
arduino
// 方式一:默认构造,初始容量为10
List<String> list1 = new ArrayList<>();
// 方式二:指定初始容量(已知数据规模时推荐,避免频繁扩容)
List<String> list2 = new ArrayList<>(20);
// 方式三:从已有集合创建
List<String> list3 = new ArrayList<>(Arrays.asList("A", "B", "C"));
csharp
List<String> list = new ArrayList<>();
// ===== 增 =====
list.add("Java"); // 尾部添加
list.add(0, "Hello"); // 指定位置插入
list.addAll(Arrays.asList("World", "!")); // 批量添加
// ===== 删 =====
list.remove(0); // 按索引删除
list.remove("Java"); // 按对象删除(只删除第一个匹配项)
list.removeIf(s -> s.length() < 3); // 条件删除(Java 8+)
list.clear(); // 清空列表
// ===== 改 =====
list.set(0, "Python"); // 修改指定位置元素
// ===== 查 =====
String item = list.get(0); // 获取指定位置元素 O(1)
int size = list.size(); // 获取元素个数
boolean contains = list.contains("Python"); // 判断是否包含某元素
int index = list.indexOf("World"); // 查找元素首次出现的位置
csharp
// 对于基本数据类型,add的时候会自动装包
List<Integer> list = new ArrayList<>();
//remove的时候,默认是索引,所以需要包装成Integer类型
list.remove(Integer.valueOf(2));
注意迭代器的遍历
vbnet
// 每次都是拿的新的迭代器,没有状态保存
// while (list.iterator().hasNext()) {
// System.out.println(list.iterator().next());
// }
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
LinkedList
- 底层双向链表
- 查询慢,要顺着链表遍历;首尾增删速度快
- 提供栈 / 队列方法:
addFirst、addLast、removeFirst - 线程不安全
arduino
// 方式一:创建空链表
LinkedList<String> list1 = new LinkedList<>();
// 方式二:从已有集合创建
LinkedList<String> list2 = new LinkedList<>(Arrays.asList("A", "B", "C"));
arduino
LinkedList<String> list = new LinkedList<>();
// ===== 增 =====
list.add("Java"); // 尾部添加
list.addFirst("Hello"); // 头部添加 O(1)
list.addLast("World"); // 尾部添加 O(1)
list.add(2, "Python"); // 指定位置插入(需先遍历定位,O(n))
// ===== 删 =====
list.remove(0); // 按索引删除
list.remove("Java"); // 按对象删除(删除第一个匹配项)
list.removeFirst(); // 删除头部 O(1)
list.removeLast(); // 删除尾部 O(1)
list.clear(); // 清空链表
// ===== 改 =====
list.set(0, "Kotlin"); // 修改指定位置元素(需先遍历定位,O(n))
// ===== 查 =====
String item = list.get(0); // 获取指定位置元素 O(n)
String first = list.getFirst(); // 获取头部元素 O(1)
String last = list.getLast(); // 获取尾部元素 O(1)
int size = list.size(); // 获取元素个数
boolean contains = list.contains("Kotlin"); // 判断是否包含某元素
int index = list.indexOf("World"); // 查找元素首次出现的位置
作为队列(Queue)使用
arduino
LinkedList<String> queue = new LinkedList<>();
// 入队(尾部添加)
queue.offer("Task1");
queue.offer("Task2");
queue.offer("Task3");
// 查看队首(不删除)
System.out.println("队首: " + queue.peek()); // Task1
// 出队(移除并返回队首)
while (!queue.isEmpty()) {
System.out.println("处理: " + queue.poll());
}
作为栈(Stack)使用
arduino
LinkedList<String> stack = new LinkedList<>();
// 压栈(头部添加)
stack.push("Data1");
stack.push("Data2");
stack.push("Data3");
// 弹栈(移除并返回头部)
while (!stack.isEmpty()) {
System.out.println("弹出: " + stack.pop());
}
遍历方式
vbnet
// 1. 增强 for 循环(最常用)
for (String s : list) {
System.out.println(s);
}
// 2. 迭代器遍历(支持边遍历边安全删除)
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String s = iterator.next();
if (s.equals("Kotlin")) {
iterator.remove(); // 安全删除
}
}
// 3. ListIterator 双向遍历(LinkedList 特有优势)
ListIterator<String> listIterator = list.listIterator();
// 正向遍历
while (listIterator.hasNext()) {
System.out.print(listIterator.next() + " ");
}
// 反向遍历
while (listIterator.hasPrevious()) {
System.out.print(listIterator.previous() + " ");
}
Vector 不推荐使用
- 底层数组;所有方法
synchronized加锁,线程安全,性能慢,基本淘汰
Set集合 & hashCode/equals
Set:无序、元素不可重复,没有索引 。常用实现类:
HashSet
csharp
HashSet<String> set = new HashSet<>();
set.add("张三");
set.add("李四");
set.add("张三"); //重复元素,添加失败,不会报错
System.out.println(set); //[张三,李四],重复被去重
-
Set没有get(index),没有普通for循环;只能用增强for、Iterator迭代器遍历
-
HashSet底层:HashMap哈希表(数组+链表/红黑树)
-
去重规则:
- 先比较
hashCode()哈希值;哈希不一样直接判定两个对象不同。 - 哈希值相同,再用
equals()做内容比较;equals返回true才判定为重复对象,拒绝存入。
- 先比较
⚠️自定义对象存入HashSet:必须重写
hashCode()和equals(),否则按内存地址判断,不会按业务属性去重。
示例学生实体:
java
class Student{
private int id;
private String name;
public Student(int id, String name) {
this.id = id;
this.name = name;
}
//必须重写这两个方法,HashSet才能按id+name去重
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return id == student.id && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name);
}
}
练习
csharp
import java.util.HashSet;
import java.util.Iterator;
import java.util.Objects;
class Hello {
public static void main(String[] args) {
HashSet<String> set = new HashSet<>();
set.add("Java");
set.add("Go");
set.add("Rust");
set.add("Java");
for (String item : set) {
System.out.println(item);
}
System.out.println("-----------------");
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
HashSet<Student> set2 = new HashSet<>();
set2.add(new Student("张三", 1001));
set2.add(new Student("李四", 1002));
set2.add(new Student("张三", 1001));
for (Student item : set2) {
System.out.println(item);
}
System.out.println("-----------------");
Iterator<Student> iterator2 = set2.iterator();
while (iterator2.hasNext()) {
System.out.println(iterator2.next());
}
}
}
class Student {
String name;
int id;
public Student(String name, int id) {
this.name = name;
this.id = id;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
Student student = (Student) obj;
return id == student.id && name.equals(student.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name);
}
}
// 小细节:`name.equals(student.name)`,如果 name 有可能为 null 会空指针;`Objects.equals(name, student.name)`更安全。
Map集合(HashMap核心)
Map存储 key‑value键值对
-
key:唯一不可重复;value允许重复
-
一个key只能对应一个value;重复put同一个key,旧value会被覆盖
-
底层HashMap:哈希表(数组+链表+红黑树),无序
Map常用API
scss
V put(K key,V value) //存入键值对,key重复覆盖旧值
V get(Object key) //根据key获取value,找不到返回null
boolean containsKey(key) //判断key是否存在
remove(key) //删除该key对应的键值对
keySet() //获取全部key的Set集合
entrySet() //获取全部键值对对象 Set<Map.Entry<K,V>>
size()
HashMap三种遍历方式
arduino
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
class Hello{
public static void main(String[] args) {
Map<Integer,String> map = new HashMap<>();
map.put(1,"张三");
map.put(2,"李四");
map.put(3,"王五");
//方式1:keySet 获取所有key,再get拿value
Set<Integer> keySet = map.keySet();
for(Integer key : keySet){
System.out.println(key + " -> " + map.get(key));
}
//方式2:entrySet,拿键值对对象(性能最好,推荐)
Set<Map.Entry<Integer,String>> entrySet = map.entrySet();
for(Map.Entry<Integer,String> entry : entrySet){
System.out.println(entry.getKey() + "=" + entry.getValue());
}
//方式3:lambda forEach(jdk8)
map.forEach((k,v)->{
System.out.println(k+":"+v);
});
}
}
重要面试点
- HashMap的key,自定义对象做key,同样必须重写
hashCode()与equals(),否则去重失效。 - HashMap线程不安全;并发环境用
ConcurrentHashMap。 - 默认初始容量16,负载因子0.75;达到
容量*0.75触发扩容,扩容为原来2倍。
📝练习任务
- 创建
HashMap<String,Integer>;put存入:"张三"‑22,"李四"‑24,"王五"‑21;重复put一次"张三"‑99观察覆盖效果。 - 使用 keySet、entrySet、lambda forEach 三种方式全部遍历打印。
- 判断key="赵六"是否存在;删除key="李四",打印map。
arduino
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
class Hello {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
map.put("张三", 22);
map.put("李四", 24);
map.put("王五", 21);
map.put("张三", 99);
// 使用 keySet、entrySet、lambda forEach 三种方式全部遍历打印
for (String key : map.keySet()) {
System.out.println(key + " -> " + map.get(key));
}
System.out.println("-----------------");
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println(entry.getKey() + " -> " + entry.getValue());
}
System.out.println("-----------------");
map.forEach((key, value) -> System.out.println(key + " -> " + value));
// 判断key="赵六"是否存在;删除key="李四",打印map。
System.out.println("赵六是否存在:" + map.containsKey("赵六"));
map.remove("李四");
System.out.println("删除李四后:" + map);
}
}