使用 Java 8 Stream 优雅的找出重复数据

cover

description: "用 Stream API 提升代码表达力"
date: 2021.12.05 10:34
categories:
- Java
tags: [Java, Stream]
keywords: stream, java, 重复, 重复数据, 重复元素


原文地址:https://wyiyi.github.io/amber/2021/11/30/stream/

最近经常遇到问题:要获取到集合中某一属性值重复的数据,除了for 循环,还有更简单得处理方式?

先来引入 Stream 流的概念。

Stream 阐述

Stream API(java.util.stream.*) 是 Java 8 中新增重要特性。
Stream 将要处理的元素集合看作一种流,由于java.util.stream.Stream 是一个 Interface ,在其中提供了函数方法,
使流在管道中进行一系列处理(如过滤,映射,聚合等)后生成的结果集合,类似于在数据库执行 SQL 语句。
这个过程通常不会对数据源造成影响。

List<String> myList = Arrays.asList("a1", "a2", "b1", "c2", "c1");
  myList.stream()
    .filter(s -> s.startsWith("c"))
    .map(String::toUpperCase)
    .sorted()
    .forEach(System.out::println);    // C1 C2

在以上示例中, 创建 Stream 流,filter,map 和 sorted 是中间操作,而 forEach 是一个终端操作。Stream操作链称为操作管道。

Stream 用法

可以从各种数据源中创建 Stream 流,其中以 Collection 集合最为常见。
如 List 和 Set 均支持 stream() 方法来创建顺序流 stream() 或者是并行流 parallelStream()。

一、常用创建 Stream 流方法

1.使用 Collection 下的 stream() 和 parallelStream() 方法来创建 Stream

List<String> list = new ArrayList<>();
Stream<String> stream = list.stream(); //获取一个顺序流
Stream<String> parallelStream = list.parallelStream(); //获取一个并行流

2.Arrays 提供了创建流的静态方法 stream(),将数组转成流

Integer[] nums = new Integer[10];
Stream<Integer> stream = Arrays.stream(nums);

3.使用 Stream 中的静态方法:of()、iterate()、generate()

Stream<Integer> stream = Stream.of(1,2,3,4,5,6);

Stream<Integer> stream2 = Stream.iterate(0, (x) -> x + 2).limit(6);
stream2.forEach(System.out::println); // 0 2 4 6 8 10
Stream<Double> stream3 = Stream.generate(Math::random).limit(2);
stream3.forEach(System.out::println);

4.使用 Pattern.splitAsStream() 方法,将字符串分隔成流

Pattern pattern = Pattern.compile(",");
Stream<String> stringStream = pattern.splitAsStream("a,b,c,d");
stringStream.forEach(System.out::println);

5.一些类也提供了创建流的方法:

IntStream.range(start, stop);
BufferedReader.lines();
Random.ints();

二、中间操作 Stream 流

1.filter:用于通过设置的条件过滤出元素,其中java.util.Objects提供了空元素的过滤

2.映射 map 方法用于映射每个元素到对应的结果

3.排序 sorted:使用 java.util.Comparator或者 reversed 更方便的对流进行升降排序

4.distinct:通过流中元素的 hashCode() 和 equals() 去除重复元素

5.skip(n):跳过n元素,配合limit(n)可实现分页

6.limit(n):用于获取指定数量的流...

三、终端操作 Stream 流

1.forEach() 迭代流中的每个元素,java.util.function.Consumer接受参数没有返回值

Stream.of(1,2,3,4,5).forEach(System.out::println);

2.collect() 可用于返回列表或字符串,java.util.Collectors 类中有求和、计算均值、取最值、字符串连接等多种收集方法。

List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd", "", "jkl");
List<String> count = strings.stream().filter(string -> string.isEmpty()).collect(Collectors.toList());
System.out.println(count.size()); // 2    

3.reduce 用于对stream中元素进行聚合求值,操作函数 accumulator 接受两个参数x,y返回r

4.anyMatch()、allMatch()、noneMatch() 接收参数Predicate,返回boolean。

5.findFirst()、findAny()、count()、max()、min() ...

使用 Stream 流解决集合中数据重复问题

我们以 Employee 为实体,对比 获取重复code值的 写法:

Employee 实体:

public class Employee extends Model<Employee> {
   @ApiModelProperty(value = "ID")
   @TableField("ID")
   private Long id;
   @ApiModelProperty(value = "编码")
   @TableId("CODE")
   private String code;
   @ApiModelProperty(value = "姓名")
   @TableField("NAME")
   private String name;
   @ApiModelProperty(value = "年龄")
   @TableField("AGE")
   private Integer age;
   ...  
}   

for 写法:

List<String> duplicate_code = new ArrayList<>();
List<Employee> employeeList = fromDB();
if (employeeList.size() > 0) {
    for (int i = 0; i < employeeList.size(); i++) {
        Employee employee = employeeList.get(i);
        for (Employee entity : employeeList) {
            String code = entity.getCode();
            if (StringUtils.isBlank(code) && StringUtils.isBlank(employee.getCode()) && code.equals(employee.getCode())) {
               duplicate_code.add(code);
            }
        }
    }
}

Stream 写法:

List<String> duplicate_code = new ArrayList<>();
List<Employee> employeeList = fromDB();
Map<Object, Long> map = employeeList.stream().collect(Collectors.groupingBy(employee -> employee.getCode(), Collectors.counting()));
Stream<Object> stringStream = map.entrySet().stream().filter(entry -> entry.getValue() > 1).map(entry -> entry.getKey());
stringStream.forEach(str -> {
   duplicate_code.add(String.valueOf(str));
});

由此可见,使用Java 8 的 Stream 流方式获取到集合中某一属性值重复数据的问题更方便、简洁!

了解更多有关 Java8 Stream 流的相关信息,请参考 Stream Javadoc 阅读官方文档

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,366评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,521评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,689评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,925评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,942评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,727评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,447评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,349评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,820评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,990评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,127评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,812评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,471评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,017评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,142评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,388评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,066评论 2 355

推荐阅读更多精彩内容