Java 学习笔记 - 集合与 Stream

Java 学习笔记 - 集合与 Stream


一、集合框架整体架构

两大分支

▼
text
复制代码
Collection 接口(单列集合,存单个元素) ↑ ├─ List 接口(有序、可重复、有下标) │ ├─ ArrayList 实现类 │ └─ LinkedList 实现类 ├─ Set 接口(不可重复) │ ├─ HashSet │ └─ TreeSet └─ Queue / Deque 队列接口 └─ LinkedList 同时实现 List + Deque Map 接口(双列集合,key-value,不属于 Collection) ├─ HashMap 实现类 ├─ TreeMap └─ LinkedHashMap

Map 不属于 Collection,是独立分支!

  • Collection:存单个对象
  • Map:存键值对(key → value)

LinkedList 多态引用场景

核心要点:引用类型决定编译期能调用哪些方法,对象本身具备全部能力。

▼
java
复制代码
// 场景1:把 LinkedList 当作普通 List 使用 List<String> list = new LinkedList<>(); // 可调用 List 的 add/get/remove 等方法 // 不能直接调用队列 offer/poll,如需使用要强制转型 // 适合:有序列表,频繁中间增删,不需要队列功能 // 场景2:使用队列、双端队列、栈 Deque<String> deque = new LinkedList<>(); // 可直接调用 offer/poll/push/pop // 不能直接调用 get(index) // 适合:消息排队、栈逻辑、头尾频繁增删

Collection 分支:抽丝剥茧的继承链

▼
text
复制代码
Iterable 接口 ↑ extends Collection 接口 ↑ extends List 接口 ↑ implements AbstractCollection 抽象类 ↑ extends AbstractList 抽象类 ↑ extends ArrayList 实现类
层级角色职责
接口(Iterable/Collection/List)立法者定义规范(方法签名),不实现
抽象类(AbstractCollection/AbstractList)包工头实现通用逻辑,留下差异化部分
具体类(ArrayList/LinkedList)工人完成具体实现

为什么这样设计? 新增一种列表只需要 extends AbstractList,重写几个方法就行。

Map 分支:抽丝剥茧的继承链

▼
text
复制代码
Map 接口 ↑ implements AbstractMap 抽象类 ↑ extends HashMap 实现类

二、ArrayList

继承与实现关系

▼
java
复制代码
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable

核心方法表

方法作用补充说明JDK
add(E e)末尾添加元素返回 boolean,永远 true1.2
add(int index, E element)指定位置插入后面元素全部后移,效率低1.2
get(int index)根据下标获取越界抛 IndexOutOfBoundsException1.2
set(int index, E element)修改指定下标元素返回旧值1.2
remove(int index)按下标删除返回被删除元素1.2
remove(Object o)删除匹配的元素(第一个)返回 boolean1.2
contains(Object o)判断是否包含底层调用 equals()1.2
indexOf(Object o)查找首次出现下标找不到返回 -11.2
lastIndexOf(Object o)查找最后一次出现下标找不到返回 -11.2
size()获取元素个数1.2
isEmpty()判断是否为空1.2
clear()清空全部元素1.2
sort(Comparator)按比较器排序JDK 8+1.8

经典代码

三种遍历方式:

▼
java
复制代码
// 需要序号 → 用索引 for (int i = 0; i < workers.size(); i++) { System.out.println((i + 1) + ". " + workers.get(i).getName()); } // 纯遍历 → for-each(推荐) for (Worker w : workers) { System.out.println(w.getName()); } // 过滤统计 → Stream workers.stream() .filter(w -> w.getScore() > 4.0) .forEach(w -> System.out.println(w.getName()));

排序:

▼
java
复制代码
// 按工龄升序 workers.sort((a, b) -> a.getWorkYear() - b.getWorkYear()); // 按评分降序 workers.sort((a, b) -> Double.compare(b.getScore(), a.getScore())); // 先按工种,再按工龄降序 workers.sort((a, b) -> { int jobCompare = a.getJobType().compareTo(b.getJobType()); if (jobCompare != 0) return jobCompare; return b.getWorkYear() - a.getWorkYear(); });

注意点

  1. 面向接口编程:声明用 List<String>,不绑死 ArrayList<String>
  2. 遍历方式按场景选:需要序号用 index,纯遍历用 for-each,过滤统计用 Stream

常见坑

  1. 遍历时直接删除元素 → ConcurrentModificationException

    ▼
    java
    复制代码
    // ❌ 错误 for (Worker w : workers) { if (w.getScore() < 3.0) workers.remove(w); } // ✅ 正确 workers.removeIf(w -> w.getScore() < 3.0);
  2. remove(Object) 和 remove(int) 重载陷阱

    ▼
    java
    复制代码
    List<Integer> nums = new ArrayList<>(); nums.add(1); nums.add(2); nums.add(3); nums.remove(1); // ❌ 按索引删,删掉的是 2 nums.remove(Integer.valueOf(1)); // ✅ 按对象删,删掉的是 1
  3. 扩容性能消耗:默认初始容量 10,满了扩容为 1.5 倍

  4. 线程不安全:多线程并发 add/remove 可能丢数据

  5. subList 是原集合视图:修改 subList 会影响原集合

号外

  • ArrayList vs LinkedList:ArrayList 查询快 O(1)、中间增删慢;LinkedList 查询慢 O(n)、首尾增删快
  • Vector 已淘汰:线程安全但已过时,不要用

三、HashMap

继承与实现关系

▼
java
复制代码
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable

核心方法表

方法作用补充说明JDK
put(K key, V value)添加/覆盖键值对key 重复则覆盖旧 value,返回旧 value1.2
get(Object key)根据 key 取值key 不存在返回 null1.2
getOrDefault(K key, V defaultValue)取值带默认值key 不存在返回默认值1.8
remove(Object key)根据 key 删除返回被删除的 value1.2
containsKey(Object key)判断是否存在 key1.2
containsValue(Object value)判断是否存在 value1.2
size()获取键值对数量1.2
isEmpty()判断是否为空1.2
clear()清空所有键值对1.2
keySet()获取全部 key 集合返回 Set<K>1.2
values()获取全部 value 集合返回 Collection<V>1.2
entrySet()获取全部键值对返回 Set<Map.Entry<K,V>>1.2

经典代码

四种遍历方式:

▼
java
复制代码
// entrySet 遍历(最常用,key-value 都要) for (Map.Entry<String, Worker> entry : map.entrySet()) { System.out.println(entry.getKey() + "=" + entry.getValue().getName()); } // keySet 遍历(只要 key) for (String key : map.keySet()) { Worker w = map.get(key); } // values 遍历(只要 value) for (Worker w : map.values()) { System.out.println(w.getName()); } // Lambda forEach map.forEach((k, v) -> System.out.println(k + ":" + v.getName()));

存在性判断(项目规范写法):

▼
java
复制代码
public static void findById(String id) { if (!map.containsKey(id)) { System.out.println("不存在:" + id); return; } System.out.println(map.get(id)); }

注意点

  1. Map 不属于 Collection:Collection 存单个元素,Map 存键值对
  2. containsKey 比 get() != null 更准确:value 可能本身就是 null
  3. HashMap 和 ArrayList 互补配合:ArrayList 有序遍历,HashMap 快速查询

常见坑

  1. 自定义对象作为 key,必须重写 equals() 和 hashCode()
  2. 线程不安全:并发 put 导致数据混乱,并发场景改用 ConcurrentHashMap
  3. 无序:遍历顺序 ≠ 存入顺序,需要顺序用 LinkedHashMap
  4. 扩容触发 rehash:元素超过阈值时扩容为 2 倍,所有元素重新计算 hash 位置

号外

  • JDK 8 优化:链表长度超过 8 且数组长度超过 64 时,链表转为红黑树
  • 负载因子默认 0.75:容量 16 时,元素达到 12 就扩容
  • HashMap vs Hashtable:Hashtable 线程安全但已废弃,ConcurrentHashMap 是并发首选

四、Collections 工具类

Collections(带 s)是工具类,Collection(不带 s)是接口。两者完全不同!

核心方法表

方法作用示例
sort(List, Comparator)按比较器排序Collections.sort(list, (a,b)->a-b)
sort(List)按自然顺序排序Collections.sort(stringList)
reverse(List)反转列表顺序Collections.reverse(list)
shuffle(List)随机打乱顺序Collections.shuffle(list)
max(Collection, Comparator)取最大值Collections.max(list, comparator)
min(Collection, Comparator)取最小值Collections.min(list, comparator)
frequency(Collection, Object)统计某元素出现次数Collections.frequency(list, "a")
binarySearch(List, Object)二分查找(列表必须先排序)Collections.binarySearch(list, key)

注意点

  1. Collections.sort() vs List.sort()

    ▼
    java
    复制代码
    Collections.sort(workers, comparator); // JDK 1.2 workers.sort(comparator); // JDK 8,两者等价
  2. Collections 只操作已有集合,不创建新集合

  3. sort 会修改原列表,不是返回新列表


五、Stream API

基础规则

▼
text
复制代码
元素 -> 逻辑
  • 箭头左边:遍历出来的每一个元素
  • 箭头右边:执行逻辑
  • 单行表达式:不用写 return
  • 大括号 {} 多行代码块:必须写 return

Stream 操作汇总对比表

方法Lambda 返回值作用
filterboolean筛选元素,保留 true
map任意对象一对一转换元素
mapToIntint元素转为 int,做 sum/average/max/min
mapToLonglong元素转为 long,做 sum/average/max/min
mapToDoubledouble元素转为 double,做 sum/average/max/min
flatMapStream一对多,扁平化拆集合
sortedint(Comparator)排序
distinct无去重
groupingBy任意对象(作为 Map 的 key)按 key 分组,相同 key 归为一组
partitioningByboolean真假二分区,只分成两组

核心区别:

  • filter 输出 boolean → 决定留不留
  • mapToXxx 输出数字 → 做统计运算
  • map 输出任意对象 → 元素转换
  • flatMap 输出 Stream → 拆内层集合

两大操作类型

类型特征例子
中间操作返回 Stream,可无限链式调用filter()、map()、sorted()
终结操作返回非 Stream 结果,触发执行forEach()、count()、sum()、collect()

核心规则:没有终结操作,中间操作全部白写!


filter 详解

作用:按条件筛选元素,返回 true 的保留,返回 false 的丢弃。元素本身不变。

函数式接口:Predicate<T> —— 接收一个对象,返回 boolean。

语法拆分:item -> item.getAmount() > 30000

部分说明
item遍历得到的每个流元素,自定义名字
->Lambda 箭头
item.getAmount() > 30000判断逻辑,必须返回 boolean

两种写法:

▼
java
复制代码
// 单行简写(不用 return) long count = quotations.stream() .filter(q -> q.calcOriginalTotalAmount() > 30000) .count(); // 大括号多行(必须写 return) .filter(q -> { double amount = q.calcOriginalTotalAmount(); return amount > 30000 && amount < 100000; })

多个 filter 串联(且关系):

▼
java
复制代码
quotations.stream() .filter(q -> q.calcOriginalTotalAmount() > 30000) .filter(q -> q.getTotalDiscountRate() < 0.9) .forEach(q -> System.out.println(q.getSummaryLine())); // 等价于一个 filter 用 && 连接 .filter(q -> q.calcOriginalTotalAmount() > 30000 && q.getTotalDiscountRate() < 0.9)

常见坑:

  1. Lambda 右边必须返回 boolean,不能返回 double 或其他类型

    ▼
    java
    复制代码
    .filter(q -> q.getAmount()) // ❌ 返回 double,编译错误 .filter(q -> q.getAmount() > 30000) // ✅ 返回 boolean
  2. filter 不会修改原集合

    ▼
    java
    复制代码
    quotations.stream().filter(...); // quotations 本身没变!
  3. 空结果不报错,只是没有元素进入后续步骤

    ▼
    java
    复制代码
    long count = quotations.stream() .filter(q -> q.getAmount() > 999999999) .count(); // 结果:0,不报错

map 详解

作用:一对一映射。流里 1 个元素 → 输出 1 个新元素。Lambda 返回任意类型。

和 mapToXxx 区分:

  • mapToXxx 产出基础类型流(IntStream/LongStream/DoubleStream)
  • 普通 map 产出 Stream<T>(包装类 / 对象流)

示例:

▼
java
复制代码
// 将 Product 转为商品名称字符串 List<String> nameList = productList.stream() .map(p -> p.getName()) .collect(Collectors.toList()); // 多行写法 .map(p -> { String name = p.getName() + "_商品"; return name; })

flatMap 详解

作用:一对多 + 扁平化。1 个元素能拆成多个元素,把内层集合"摊开"。

Lambda 返回:Stream 流(重点!)

场景:元素里面包含集合,需要把里面集合全部拆出来变成一整条流。

示例:订单里包含多个商品

▼
java
复制代码
class Order { private String orderNo; private List<Product> productList; // 一个订单包含多个商品 public List<Product> getProductList() { return productList; } } // 把所有订单里面全部商品提取出来,合并成一个商品流 List<Product> allProducts = orderList.stream() .flatMap(order -> order.getProductList().stream()) .collect(Collectors.toList());

map 和 flatMap 直观对比:

方法Lambda 返回映射关系核心作用
map任意对象一对一元素转换,不拆集合
flatMapStream<R>一对多 + 扁平化拆内层集合,摊平流
▼
java
复制代码
// map:返回 Stream<List<Product>>,流里放的是一个个 List,没有摊开 // 结构:[List1, List2, List3] orderList.stream() .map(order -> order.getProductList()) // flatMap:返回 Stream<Product>,把 List 拆开,所有商品全部摊平 // 结构:商品1, 商品2, 商品3... orderList.stream() .flatMap(order -> order.getProductList().stream())

常见坑:

  • flatMap 的 Lambda 必须返回 Stream,不能直接返回 List!
    ▼
    java
    复制代码
    ❌ .flatMap(o -> o.getProductList()) // 返回 List,编译错误 ✅ .flatMap(o -> o.getProductList().stream()) // 返回 Stream

mapToDouble 详解

作用:把 Stream<对象> 转成 DoubleStream(基础类型流),才能调用 sum()、average()、max()、min() 等数学运算。

函数式接口:ToDoubleFunction<T> —— 接收一个对象,返回 double。

语法拆分:item -> item.getPrice()

部分说明
item遍历得到的每个流元素
->Lambda 箭头
item.getPrice()转换逻辑,必须返回 double

三种基础类型流对比:

方法函数式接口返回流类型Lambda 返回要求
mapToIntToIntFunctionIntStreamint
mapToLongToLongFunctionLongStreamlong
mapToDoubleToDoubleFunctionDoubleStreamdouble

示例:

▼
java
复制代码
// 提取金额求和 double total = orderList.stream() .mapToDouble(order -> order.getPrice()) .sum(); // 多行写法(打8折) double discountSum = orderList.stream() .mapToDouble(order -> { double originPrice = order.getPrice(); double after = originPrice * 0.8; return after; }) .sum(); // filter + mapToDouble 搭配 double sum = orderList.stream() .filter(order -> order.getPrice() > 100) .mapToDouble(order -> order.getPrice()) .sum();

DoubleStream 专属终端方法:

方法返回类型作用
.sum()double求和
.max()OptionalDouble最大值
.min()OptionalDouble最小值
.average()OptionalDouble平均值

安全取值(OptionalDouble):

▼
java
复制代码
double avg = quotations.stream() .mapToDouble(q -> q.calcOriginalTotalAmount()) .average() .orElse(0); // 如果流为空,返回 0

基础流转回包装类流:

▼
java
复制代码
Stream<Integer> boxedStream = intStream.boxed(); // IntStream → Stream<Integer>

常见坑:

  1. Lambda 右边必须返回 double,不能返回 boolean

    ▼
    java
    复制代码
    .mapToDouble(q -> q.getAmount() > 30000) // ❌ 返回 boolean,编译错误 .mapToDouble(q -> q.getAmount()) // ✅ 返回 double
  2. mapToDouble 之后得到的是 DoubleStream,不是 Stream<Double>

    • Stream<Double> 没有 sum() 方法
    • DoubleStream 才有 sum()、average()
  3. 不会修改原集合元素,只是读取转换


groupingBy 详解

作用:把流中的元素按某个规则分组,装进 Map 里。key 是分组标识,value 是每组的汇总结果。

语法拆分:

▼
java
复制代码
Collectors.groupingBy( 元素 -> 分组key, // Function<T, K>:每个元素属于哪一组 Collectors.xxx() // Collector:每组怎么统计 )

两种用法:

▼
java
复制代码
// 用法1:只传分组规则 → 结果是 Map<K, List<T>> Map<String, List<Worker>> byJob = workers.stream() .collect(Collectors.groupingBy(w -> w.getJobType())); // 用法2:分组规则 + 统计方式 → 结果是 Map<K, 统计结果> Map<String, Long> countByJob = workers.stream() .collect(Collectors.groupingBy( w -> w.getJobType(), Collectors.counting() ));

常用统计方式:

统计方式每组做什么结果类型
Collectors.counting()数有几个Map<K, Long>
Collectors.summingDouble(fn)求和Map<K, Double>
Collectors.averagingDouble(fn)算平均Map<K, Double>
Collectors.toList()收集成列表Map<K, List<T>>
Collectors.maxBy(Comparator)找最大Map<K, Optional<T>>
Collectors.minBy(Comparator)找最小Map<K, Optional<T>>

完整示例:

▼
java
复制代码
// 按工种分组,统计每类师傅的评分总和 Map<String, Double> sumByJob = workers.stream() .collect(Collectors.groupingBy( w -> w.getJobType(), Collectors.summingDouble(w -> w.getScore()) )); // 按客户类型分组,统计每类有几单 Map<String, Long> countByType = quotations.stream() .collect(Collectors.groupingBy( q -> q.getClient() instanceof Customer ? "业主" : "公司客户", Collectors.counting() )); // 复杂条件分组 Map<String, List<Product>> map = list.stream() .collect(Collectors.groupingBy(p -> { if (p.getPrice() > 1000) return "高价商品"; else return "低价商品"; }));

多字段分组(嵌套 groupingBy):

▼
java
复制代码
Map<String, Map<String, List<Worker>>> nested = workers.stream() .collect(Collectors.groupingBy( w -> w.getJobType(), Collectors.groupingBy( w -> w.getScore() > 4.0 ? "高分" : "普通" ) )); // 结果:{布线工={高分=[赵孙钱], 普通=[...]}, 安装工={...}}

常见坑:

  1. 只传一个参数时,结果是 Map<K, List<T>>,不是计数!
  2. groupingBy 的 key 可以为 null,业务尽量避免
  3. groupingBy 不保证顺序,需要顺序用 LinkedHashMap
  4. collect 是终端操作,执行后不能继续调用 stream 中间方法

partitioningBy 详解

作用:特殊的分组,永远只有 true / false 两组,返回 Map<Boolean, List<元素>>。适合二分类。

和 groupingBy 对比:

方法Lambda 返回分组数量返回 Map 的 key
groupingBy任意类型多组(不限)自定义对象(String/Integer 等)
partitioningByboolean固定 2 组true / false

示例:

▼
java
复制代码
// 价格大于1000为true,其余false Map<Boolean, List<Product>> partMap = list.stream() .collect(Collectors.partitioningBy(p -> p.getPrice() > 1000)); // 分区 + 统计数量 Map<Boolean, Long> countMap = list.stream() .collect(Collectors.partitioningBy( p -> p.getPrice() > 1000, Collectors.counting() )); // 结果遍历 countMap.forEach((isHigh, num) -> { System.out.println((isHigh ? "高价" : "低价") + ":" + num + "个"); });

Collectors 详解

Collectors 是 Stream 流水线的**"打包车间"**——Stream 负责加工数据,Collectors 负责把加工好的数据装进容器。

核心打包方式:

方法打包结果示例
Collectors.toList()装进 ArrayListList<String> list = stream.collect(Collectors.toList())
Collectors.toSet()装进 HashSet(去重)Set<String> set = stream.collect(Collectors.toSet())
Collectors.toMap(keyFn, valFn)装进 HashMapMap<String, Double> map = stream.collect(Collectors.toMap(q -> q.getId(), q -> q.getAmount()))
Collectors.groupingBy(...)按条件分组装 Map见上方 groupingBy 详解
Collectors.partitioningBy(...)真假二分区见上方 partitioningBy 详解
Collectors.counting()计数.collect(Collectors.counting())
Collectors.summingDouble(fn)求和.collect(Collectors.summingDouble(q -> q.getAmount()))
Collectors.averagingDouble(fn)平均值.collect(Collectors.averagingDouble(q -> q.getAmount()))
Collectors.joining(",")用逗号拼接字符串stream.collect(Collectors.joining(","))

Collectors vs Collections 区分:

CollectorsCollections
给谁用Stream 的 collect() 方法普通 Collection(List/Set/Map)
什么时候用Stream 流水线结尾直接操作已有集合
典型用法.collect(Collectors.toList())Collections.sort(list)
本质定义"怎么打包"定义"怎么操作"

stream().filter().count() vs Collections.frequency() 对比

对比项stream().filter().count()Collections.frequency()
做什么按任意条件统计满足的有几个统计某个具体值出现了几次
参数Lambda 条件(> < instanceof 都行)一个精确匹配的对象
比较逻辑你的条件表达式只能用 equals() 精确匹配
返回类型longint
适用对象Stream 流普通 Collection(List/Set)
自定义对象✅ 好用❌ 基本不能用(比的是对象地址)

场景1:按条件统计 → 用 Stream

▼
java
复制代码
long count = quotations.stream() .filter(q -> q.calcOriginalTotalAmount() > 30000) .count();

场景2:精确匹配某个值出现几次 → 用 Collections.frequency

▼
java
复制代码
List<String> names = Arrays.asList("赵孙钱", "钱多多", "赵孙钱", "李四海"); int times = Collections.frequency(names, "赵孙钱"); // 结果:2

Stream 常见坑汇总

  1. Stream 中间操作不会修改原集合,只是读取。

    ▼
    java
    复制代码
    quotations.stream().sorted(...); // quotations 本身顺序不变
  2. 没有终结操作,中间操作不执行(惰性求值)。

    ▼
    java
    复制代码
    quotations.stream().filter(q -> { System.out.println("过滤"); return true; }); // ❌ 没有任何打印
  3. Stream 只能消费一次。

    ▼
    java
    复制代码
    Stream<SmartHomeQuotation> s = quotations.stream(); s.forEach(...); // 第一次消费 s.forEach(...); // ❌ 报错:stream has already been operated upon
  4. collect 是终端操作,执行后不能继续调用 stream 中间方法。

    ▼
    java
    复制代码
    list.stream().collect(Collectors.toList()).stream().filter(...); // 要先 collect,再重新 stream()
  5. groupingBy / partitioningBy 的 key 可以为 null,业务尽量避免。

  6. 基础流 IntStream/LongStream/DoubleStream 和包装类流 Stream<Integer> 不是同一个类型。

    • Stream<Integer> 没有 sum() 方法
    • IntStream 才有 sum()、average()
  7. flatMap 必须返回 Stream,不能直接返回集合。

    ▼
    java
    复制代码
    ❌ .flatMap(o -> o.getProductList()) // 返回 List,编译错误 ✅ .flatMap(o -> o.getProductList().stream()) // 返回 Stream
  8. 在 Stream 的 Lambda 中修改外部变量 → 编译错误。

    ▼
    java
    复制代码
    int sum = 0; list.stream().forEach(x -> sum += x); // ❌ 编译错误 // 正确做法:用 mapToDouble + sum()

六、Lambda 表达式

基础介绍

Lambda 是 JDK 8 引入的匿名函数,用于简化函数式接口(只有一个抽象方法的接口)的实现。

基础语法

▼
java
复制代码
// 无参数 () -> System.out.println("Hello") // 一个参数,类型可省略 x -> x * 2 // 多个参数 (a, b) -> a + b // 多条语句用 {} 包裹 (x, y) -> { int sum = x + y; return sum; }

常用函数式接口

接口方法Lambda 示例Stream 中对应
Predicate<T>test(T)x -> x > 0filter
Function<T,R>apply(T)x -> x.toString()map
Consumer<T>accept(T)x -> System.out.println(x)forEach
Supplier<T>get()() -> new Random().nextInt()创建 Stream
ToDoubleFunction<T>applyAsDouble(T)x -> x.getPrice()mapToDouble

方法引用 ::

类型写法等价 Lambda使用场景
对象方法引用对象::方法x -> 对象.方法(x)固定对象调方法
类方法引用类名::方法x -> x.方法()Stream 元素调自身方法
静态方法引用类名::静态方法(a,b) -> 类名.静态方法(a,b)工具类方法
构造器引用类名::new() -> new 类名()创建对象
▼
java
复制代码
// 类名::实例方法 → Stream 中常用 workers.stream().map(Worker::getName) // 等价于:workers.stream().map(w -> w.getName())

注意点

  1. Lambda 只能用于函数式接口
  2. Lambda 中不能修改外部局部变量(必须是 effectively final)

七、Comparator 排序

工作原理

compare(a, b) 返回 int,排序器根据这个 int 决定谁在前谁在后。

compare(a, b) 返回值含义结果
负数a 排在 b 前面a 赢了
正数a 排在 b 后面b 赢了
0a 和 b 相等顺序不变

口诀

a - b → 负数 → a 在前 → 升序(从小到大) b - a → 正数 → b 在前 → 降序(从大到小)

经典代码

▼
java
复制代码
// 按工龄升序:正常减 Collections.sort(workers, (a, b) -> a.getWorkYear() - b.getWorkYear()); // 按评分降序:反过来比(double 用 Double.compare) Collections.sort(workers, (a, b) -> Double.compare(b.getScore(), a.getScore())); // 按姓名拼音升序:String 自带 compareTo Collections.sort(workers, (a, b) -> a.getName().compareTo(b.getName())); // 多条件排序:先按工种,再按工龄降序 Collections.sort(workers, (a, b) -> { int jobCompare = a.getJobType().compareTo(b.getJobType()); if (jobCompare != 0) return jobCompare; // 工种不同,按工种排 return b.getWorkYear() - a.getWorkYear(); // 工种相同,按工龄降序 }); // Comparator 链式写法(隐藏 if 逻辑) workers.sort(Comparator .comparing(Worker::getJobType) .thenComparing((a, b) -> b.getWorkYear() - a.getWorkYear()));

附录:集合框架速查

选择哪个集合?

需求选择理由
有序列表,按顺序遍历ArrayList动态数组,查询快
频繁在头部/尾部增删LinkedList双向链表,首尾操作快
键值对,按 key 快速查找HashMap哈希表,O(1) 查找
键值对,需要保持存入顺序LinkedHashMap链表维护插入顺序
去重,判断是否存在HashSet基于 HashMap 实现

三级流水线

▼
text
复制代码
数据库(查) → Stream(加工) → Collectors(打包) ↓ ↓ ↓ 原始数据 filter/map List/Map/数字

常见遍历方式对比

▼
java
复制代码
// ArrayList 遍历 for (int i = 0; i < list.size(); i++) // 需要序号 for (Worker w : workers) // 纯遍历 workers.stream().filter(...).forEach(...) // 过滤统计 // HashMap 遍历 for (Map.Entry<K,V> entry : map.entrySet()) // key-value 都要 for (K key : map.keySet()) // 只要 key for (V value : map.values()) // 只要 value map.forEach((k, v) -> ...) // Lambda
0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
下载 APP