277. Java Stream API - 去重与排序:Stream 中的 distinct() 与 sorted()

文章目录

  • [277. Java Stream API - 去重与排序:Stream 中的 `distinct()` 与 `sorted()`](#277. Java Stream API - 去重与排序:Stream 中的 distinct() 与 sorted())
      • [🎯 目标](#🎯 目标)
    • [🧼 `distinct()` ------ 去重利器](#🧼 distinct() —— 去重利器)
      • [📌 定义:](#📌 定义:)
      • [🔍 `distinct()` 的内部原理](#🔍 distinct() 的内部原理)
      • [✅ 示例:整数去重](#✅ 示例:整数去重)
      • [🔽 输出:](#🔽 输出:)
    • [📊 `sorted()` ------ 排序操作](#📊 sorted() —— 排序操作)
      • [📌 定义:](#📌 定义:)
      • [🧠 工作机制:](#🧠 工作机制:)
      • [✅ 示例 1:自然排序(String 实现了 Comparable)](#✅ 示例 1:自然排序(String 实现了 Comparable))
      • [🔽 输出:](#🔽 输出:)
      • [✅ 示例 2:按长度排序(使用 Comparator)](#✅ 示例 2:按长度排序(使用 Comparator))
      • [🔽 输出:](#🔽 输出:)
    • [🔁 无限流中的表现对比](#🔁 无限流中的表现对比)
      • [✅ `distinct()` 适用于无限流](#✅ distinct() 适用于无限流)
      • [🔽 输出:](#🔽 输出:)
      • [❌ `sorted()` 不适用于无限流](#❌ sorted() 不适用于无限流)
      • [⚠️ 输出:](#⚠️ 输出:)
    • [✅ 总结对比](#✅ 总结对比)
    • [🧠 延伸小技巧](#🧠 延伸小技巧)
      • [🧩 想去重自定义对象?](#🧩 想去重自定义对象?)

277. Java Stream API - 去重与排序:Stream 中的 distinct() 与 sorted()


🎯 目标

本节将讲解如何使用 distinct() 去除重复值,使用 sorted() 对流元素排序,包括:

  • 它们的工作机制
  • 使用示例
  • 注意事项(如可用于无限流与否)
  • 性能影响

🧼 distinct() ------ 去重利器

📌 定义:

distinct() 会移除流中重复的元素,依据是元素的:

  • hashCode() 和
  • equals()

所以:你使用的对象必须正确实现这两个方法,才能保证去重有效。


🔍 distinct() 的内部原理

distinct() 并不像大多数中间操作那样是"无状态"的:

  • 它内部维护一个 Set 集合来追踪已见过的元素。
  • 每处理一个元素,会尝试把它加入 Set。
    • ✅ 加入成功:表示是第一次出现,传递下游。
    • ❌ 加入失败:说明是重复的,直接丢弃。

⚠️ 因为要维护状态,所以它会占用内存 ,但能即时输出结果。


✅ 示例:整数去重

java 复制代码
List<Integer> ints = List.of(1, 4, 2, 1, 3, 3);

List<Integer> distincts = ints.stream()
                              .distinct()
                              .toList();

System.out.println("distinct ints: " + distincts);

🔽 输出:

java 复制代码
distinct ints: [1, 4, 2, 3]

✅ 说明:重复的 1 和 3 被移除了,且保持原始顺序。


📊 sorted() ------ 排序操作

📌 定义:

sorted() 用于将流中的元素排序。

它有两个重载:

java 复制代码
Stream<T> sorted();                          // 自然顺序(元素必须实现 Comparable)
Stream<T> sorted(Comparator<? super T> c);   // 自定义顺序

🧠 工作机制:

  • sorted() 是有状态的中间操作。
  • 它必须读取所有元素后再排序输出。
  • 所以它不适用于无限流,会导致挂起或卡死。

✅ 示例 1:自然排序(String 实现了 Comparable)

java 复制代码
List<String> strings = List.of("one", "two", "three", "four");

List<String> naturalSort = strings.stream()
                                  .sorted()
                                  .toList();

System.out.println("natural sort: " + naturalSort);

🔽 输出:

java 复制代码
natural sort: [four, one, three, two]

👉 字符串按照字典序排序。


✅ 示例 2:按长度排序(使用 Comparator)

java 复制代码
List<String> shortestFirst = strings.stream()
                                    .sorted(Comparator.comparingInt(String::length))
                                    .toList();

System.out.println("shortest first: " + shortestFirst);

🔽 输出:

java 复制代码
shortest first: [one, two, four, three]

👉 按字符串长度升序排序。


🔁 无限流中的表现对比

✅ distinct() 适用于无限流

它可以边处理边输出 ,只要有足够的终止条件(如 limit()),就不会卡死。

示例:
java 复制代码
var ints = IntStream.iterate(0, i -> i + 1)
                    .map(i -> i / 3)
                    .distinct()
                    .limit(5)
                    .toArray();

System.out.println("ints = " + Arrays.toString(ints));

🔽 输出:

java 复制代码
ints = [0, 1, 2, 3, 4]

🌟 它发现新值就会推送给下游,表现非常高效!


❌ sorted() 不适用于无限流

因为它必须先收集所有元素再排序,所以当流无限时,它永远等不到结束,程序会卡死或耗尽内存。

示例(反例):
java 复制代码
int[] ints = IntStream.iterate(0, i -> i + 1)
                    .map(i -> i / 3)
                    .sorted()
                    .limit(5)
                    .toArray();

System.out.println("ints = " + Arrays.toString(ints));

⚠️ 输出:

java 复制代码
<程序将无限等待,永远没有结果>

✅ 总结对比

方法 是否有状态 是否可用于无限流 内部机制
distinct() 有状态 ✅ 支持 使用 Set 去重
sorted() 有状态 ❌ 不支持 使用缓冲区收集+排序

🧠 延伸小技巧

🧩 想去重自定义对象?

你的类必须实现正确的:

  • equals()
  • hashCode()

示例:

java 复制代码
record User(String name, int age) {}
List<User> users = ...;
users.stream().distinct().toList(); // 正常去重

如用的是 Comparator 排序:

java 复制代码
users.stream()
     .sorted(Comparator.comparing(User::age))
     .toList();
相关推荐
Dovis(誓平步青云)1 分钟前
总用电下降了,为什么家庭能源看板仍然提示异常
java·开发语言·数据库·人工智能·算法·电脑·能源
程序猿乐锅1 分钟前
【从0-1学习Python | 第四篇】Python 字符串和元组
java·开发语言·笔记·后端·python·学习
开开心心就好5 分钟前
Windows视频播放器推荐,精简版体积更小
java·服务器·开发语言·eclipse·pdf·ocr·音视频
开开心心就好5 分钟前
A3的PDF怎么切成A4?拖一下分割线就行
服务器·开发语言·python·算法·pdf·ocr·tornado
小黄人软件7 分钟前
python 模块(Module)与包(Package)的区别,python xxx.py、python -m xxx 和 import xxx 三者的区别
开发语言·python
PHP实战开发录7 分钟前
PHP中文文件名为什么乱码
开发语言·php
Rain的Java大神之路27 分钟前
10 万人抢 2000 个座位,电影票选座系统怎么防超卖?一文讲透大厂高频场景题
java·后端·面试
阿里嘎多学长31 分钟前
2026-10-13 GitHub 热点项目精选
开发语言·程序员·github·代码托管
砚底藏山河33 分钟前
量化新手误区:因子计算与行情数据的工程化解耦
java·数据库·python·金融
威哥爱编程35 分钟前
【AI全栈12-07】Spring Boot 用 Agent 自动完成多步运营任务
java·spring boot·后端