发布于2026-07-09 阅读(0)
扫一扫,手机访问
一句话总结:Arrays.stream().distinct()对引用类型数组天生有效,对基本类型需要先转包装流;自定义对象必须正确重写equals和hashCode才能按值去重;并且可以一条链式调用搞定去重、映射和收集。
很多人在处理数组去重时,第一个想到的就是 Arrays.stream().distinct()。但关键要看清楚:你传进去的“原始数组”是什么类型。对于 String[]、Integer[] 这类引用类型数组,它能直接识别出重复元素,一个 distinct() 就搞定了。可一旦换成 int[]、double[] 这样的基本类型数组,情况就变了——如果不做特殊处理,Stream 会傻乎乎地把整个数组当成一个对象来处理,根本不会去遍历里面的元素,更别说去重了。

举个例子,String[] 或 Integer[] 这类数组,每个元素自带合理的 equals/hashCode 实现,distinct() 能按值去重,代码简洁直观:
String[] arr = {"a", "b", "a", "c"};
String[] unique = Arrays.stream(arr)
.distinct()
.toArray(String[]::new); // ["a", "b", "c"]
你甚至不需要写任何额外的比较逻辑,一行流操作就完成了去重与收集。
当数组是 int[] 这类基本类型时,Arrays.stream() 返回的是 IntStream。好消息是 IntStream.distinct() 本身是有效的——但它返回的仍然是 IntStream。如果你想把它转回 int[],直接 .distinct().toArray() 就行。但更多场景下我们需要的是 Integer[] 或 List,这时就必须调用 boxed() 把基本类型装箱:
IntStream.distinct().toArray()IntStream.distinct().boxed().toArray(Integer[]::new)IntStream.distinct().boxed().collect(Collectors.toList())int[] nums = {1, 2, 2, 3, 1};
int[] uniqueInts = Arrays.stream(nums).distinct().toArray(); // [1, 2, 3]
Integer[] uniqueBoxed = Arrays.stream(nums)
.distinct()
.boxed()
.toArray(Integer[]::new); // [1, 2, 3]
注意不要在 IntStream 上直接调用 .toArray(Integer[]::new),那会编译报错——必须先装箱。
对于自定义类(比如 User[]),distinct() 能去重的前提是该类正确重写了 equals() 和 hashCode()。否则即使两个对象内容完全相同,只要内存地址不同,就不会被当成重复元素移除。
看看反面例子:
User u1 = new User("Alice", 25);
User u2 = new User("Alice", 25); // 内容相同但不同对象
User[] users = {u1, u2};
// Arrays.stream(users).distinct().count() → 2(不是 1!)
这一点经常被忽略,但只要记住一条原则:任何需要按值比较的场景,都别忘记给自定义类实现 equals/hashCode,否则 distinct() 等于白用。
实际开发中我们经常需要“先去重、再转换、最后收集”的一连串操作。这时候链式写法就体现出优势了:
String[] arr = {"hello", "world", "hello"};
List result = Arrays.stream(arr)
.distinct()
.map(String::toUpperCase)
.collect(Collectors.toList()); // ["HELLO", "WORLD"]
这种写法清晰、不可变、没有副作用,是函数式去重转换的推荐范式。只要记住上述几个注意点(引用类型直接去重、基本类型先装箱、自定义对象重写 equals),你就能用 Arrays.stream().distinct() 把数组去重这件事做得简洁又优雅。