JDK8-2-流(2.1)- 流操作-distinct

初识 JDK1.8 stream流 阅读详情

JDK8-2-流(2.1)- 流操作-distinct

去重操作,如下开头两个菜品一样,对 menu 去重如下:

public class DishDistinctTest1 {

    public static final List<Dish> menu = Arrays.asList(
            new Dish("pork", false, 800, Dish.Type.MEAT),
            new Dish("pork", false, 800, Dish.Type.MEAT),
            new Dish("beef", false, 700, Dish.Type.MEAT),
            new Dish("chicken", false, 400, Dish.Type.MEAT),
            new Dish("french fries", true, 530, Dish.Type.OTHER),
            new Dish("rice", true, 350, Dish.Type.OTHER),
            //季节水果
            new Dish("season fruit", true, 120, Dish.Type.OTHER),
            new Dish("pizza", true, 550, Dish.Type.OTHER),
            //虾
            new Dish("prawns", false, 300, Dish.Type.FISH),
            //鲑鱼,三文鱼,
            new Dish("salmon", false, 450, Dish.Type.FISH));


    public static void distinctTest() {
        List<Dish> dishList = menu.stream()
                .distinct()
                .collect(Collectors.toList());
        System.out.println(dishList);
    }

    public static void main(String[] args) {
        distinctTest();
    }
}

注意: 对象去重需要重写 equals 和 hashCode 方法(默认对象 equals 方法比较的是对象内存地址是否一致),由 distinct 内部具体实现类 java.util.stream.DistinctOps 可以看出这点。

java.util.HashSet

public boolean contains(Object o) {
    return map.containsKey(o);
}

java.util.HashMap

public boolean containsKey(Object key) {
    return getNode(hash(key), key) != null;
}

IDEA 如何自动生成 equals 和 hashCode 方法

空白处右键选择 Generate 或者Alt + Ins 快捷键

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    Dish dish = (Dish) o;
    return vegetarian == dish.vegetarian && calories == dish.calories && Objects.equals(name, dish.name) && type == dish.type;
}

@Override
public int hashCode() {
    return Objects.hash(name, vegetarian, calories, type);
}

使用 filter 方法去重

假设现在需求有变,菜品 Dish 只要名称、是否素食、类型一致即可判断为重复,最直接的方法就是改动 Dish 类中 equals 和 hashCode 方法实现,但是这样会改变原有的规则,导致原来的代码有问题,而且假设还有其他去重的逻辑,那也无法同时满足。

如何实现

① 定义一个 String 类型 的 Set
② 将 Dish 中 name、vegetarian、type 属性拼接成字符串加入到 set 中,利用 Set 集合中无法添加重复元素的特性过滤掉没有成功添加的元素
代码如下:

public class DishDistinctTest2 {

    public static final List<Dish> REPEATED_DISHES = Arrays.asList(
            new Dish("pork", false, 790, Dish.Type.MEAT),
            new Dish("pork", false, 800, Dish.Type.MEAT),
            new Dish("beef", false, 700, Dish.Type.MEAT),
            new Dish("beef", false, 690, Dish.Type.MEAT)
    );

    private static <T> Predicate<T> distinctByKey(Function<? super T, String> keyExtractor) {
        Set<String> set = ConcurrentHashMap.newKeySet();
        return t -> set.add(keyExtractor.apply(t));
    }

    public static void distinctTest2() {
        List<Dish> dishList = REPEATED_DISHES.stream()
                .filter(
                        distinctByKey(
                                dish -> String.join("-",
                                        dish.getName(), Boolean.toString(dish.isVegetarian()), dish.getType().toString()
                                )
                        )
                )
                .collect(Collectors.toList());
        System.out.println(dishList);
    }

    public static void main(String[] args) {
        distinctTest2();
    }
}

打印结果:

[Dish{name='pork', vegetarian=false, calories=790, type=MEAT}, Dish{name='beef', vegetarian=false, calories=700, type=MEAT}]

其中重点代码为 distinctByKey 方法
如果难以理解的话,可以将以上代码用 JDK7 的方式写,如下:

public static void distinctTestWithJDK7() {
    List<Dish> dishList = new ArrayList<>();
    Set<String> set = ConcurrentHashMap.newKeySet();
    for (Dish dish : REPEATED_DISHES) {
        String key = String.join("-", dish.getName(), Boolean.toString(dish.isVegetarian()), dish.getType().toString());
        if (set.add(key)) {
            dishList.add(dish);
        }
    }
    System.out.println(dishList);
}
重复的元素如何保留想要的那一个

① new Dish(“pork”, false, 790, Dish.Type.MEAT),
② new Dish(“pork”, false, 800, Dish.Type.MEAT),
③ new Dish(“beef”, false, 700, Dish.Type.MEAT),
④ new Dish(“beef”, false, 690, Dish.Type.MEAT)

从上面例子的打印结果可以看出保留的是①、③号元素,即两个元素如果重复则保留顺序靠前的,假设现在需求要保留低卡路里的呢
很容易可以想到可以先按照卡路里排序再去重,代码如下:

public static void distinctTest3() {
    Comparator<Dish> comparator = Comparator.comparing(Dish::getName).reversed().thenComparing(Dish::getCalories);
    List<Dish> dishList = REPEATED_DISHES.stream()
            .sorted(comparator)
            .filter(distinctByKey(dish -> String.join("-", dish.getName(), Boolean.toString(dish.isVegetarian()), dish.getType().toString())))
            .collect(Collectors.toList());
    System.out.println(dishList);
}
JDK 8 中Stream流中去重的方法 JDK8中Stream流中去重的方法。 阅读详情

相关推荐

JDK 1.8新特性 日常总结

1、用JDK1.8 Stream中对List进行去重 list = list.stream().distinct().collect(Collectors.toList()); List<String> names = Arrays.asList("A",) 2、接口的默认方法 Java8允许我们给接口添加一个非抽象的方法实现,只需要使用 default关键字即可,这个...

做最专业的总结 大师 1171

【Java基础】stream流中distinct去重失败的原因

分析Stream流中distinct()去重失败的原因!

jayxiaoxionger的博客 2115

【JavaEE基础与高级 第47章】JDK1.8中的Stream流源码中的常用方法全面解析附代码演示及源码展示

文章对你有所帮助的话,欢迎给个赞或者 star,你的支持是对作者最大的鼓励,不足之处可以在评论区多多指正,交流学习。

KJ.JK 1万+

java8 :: 用法 (JDK8 双冒号用法)

java8 :: 用法 (JDK8 双冒号用法) JDK8中有双冒号的用法,就是把方法当做参数传到stream内部,使stream的每个元素都传入到该方法里面执行一下。 代码其实很简单: 以前的代码一般是如此的: public class AcceptMethod { public static void printValur(String str){ System....

java_sha的博客 1604

Ubuntu 20.04下大陆ARS408毫米波雷达点云数据采集与RVIZ实时可视化全流程解析

本文详细解析了在Ubuntu 20.04系统下,如何实现大陆ARS408毫米波雷达的点云数据采集与RVIZ实时可视化全流程。内容涵盖硬件连接、ROS Noetic环境搭建、CAN接口配置、数据验证及自动化脚本编写,为机器人感知、自动驾驶等领域的开发者提供了一套稳定、实用的实战方案。

bash7scripter的博客 698

ArcGIS+PLUS+InVEST三件套实战:从零搞定土地利用变化与碳储量预测(附完整数据包)

本文详细介绍了如何整合ArcGIS、PLUS模型和InVEST模型,构建土地利用变化与碳储量预测的完整研究流水线。通过多情景模拟和生态系统服务评估,提供从数据准备到结果验证的实战指南,助力研究者高效完成生态评估项目。

weixin_30399821的博客 541

JDK8 Stream distinct 去重 及自定义去重

在 JDK8 中引入了 Stream 的编程风格,通过灵活运用该风格可以帮助我们实现更加便捷的数据处理操作。今天这里讲解一下 Stream 中的 distinct() 实现去重以及如何通过filter()设计自定义的去重逻辑。 final int[] distinct = Stream.of(1, 1, 1, 2, 2, 3, 3, 4, 4, 5) // 根据 Objects.equal() 进行去重 .distinct() .mapToInt(Intege

永远相信美好的事情即将发生 7771

Eplan P8自动线号生成全攻略:从电位定义点到连接编号的完整流程

本文详细解析了Eplan P8自动线号生成的全流程,从电位定义点的创建配置到连接编号的标准化管理,帮助电气设计人员提升图纸规范性和工作效率。重点介绍了自动编号方案的定义与调整技巧,以及企业级标准化实施建议,是掌握Eplan P8自动线号功能的实用指南。

weixin_29254029的博客 507

JDK8新特性(八)之Stream流的map()、sorted()、distinct()、match()方法

Stream流的map()、flatMap()、sorted()、distinct()、match()方法

北辰 7376

maya中的几个热盒(空格、shift、w键)

maya中许多功能打包放进了不同的热盒中,让我们使用起来更加的便捷,熟练掌握后可以大大提升了建模速度。按下不同按键后鼠标不需要精确点到某个图标,往某个方向滑滑滑就打开了相应功能,非常的炫酷和迅猛。

m0_71983242的博客 1755

4路QSFP28接口能玩出什么花样?基于璞致VU13P开发板的100G网络测试方案

本文详细介绍了基于璞致PZ-VU13P-KFB开发板构建100G网络测试方案的实战指南。该方案充分利用板载Xilinx Virtex UltraScale+ FPGA的强大算力与4路QSFP28接口,涵盖从光模块选型、Aurora协议配置、开源Corundum方案部署,到误码率测试及散热优化的全链路流程,为高速网络设备研发与验证提供了可靠的硬件平台。

cola5的博客 452

Java8流的Collectors知识点补充

Java8 新特性Collectors

Blue_Pepsi_Cola的博客 1268

《Python星球日记》 第73天:情感分析与主题建模

《Python星球日记》 第73天:情感分析与主题建模,今天我们将探索两个自然语言处理领域的重要技术:情感分析和主题建模。这两项技术在社交媒体分析、用户反馈处理、市场研究等多个领域有着广泛应用。

Code_流苏:在代码中寻诗意,在实践中觅真知 917

java list map 去重复_Java中List集合去除重复数据的六种方法

1. 循环list中的所有元素然后删除重复public static List removeDuplicate(List list) {for ( int i = 0 ; i < list.size() - 1 ; i ++ ) {for ( int j = list.size() - 1 ; j > i; j -- ) {if ...

weixin_42556197的博客 5692

Docker:快速部署 Temporal 工作流引擎的技术指南

Temporal是一个开源的分布式工作流引擎,适用于复杂业务流程(如订单处理、支付流程等),提供状态持久化、自动重试、多语言SDK支持等特性。部署简单,支持Docker Compose,包含核心服务、Web UI及数据库。以TypeScript为例,开发者可快速编写工作流(如订单处理)和Worker,通过CLI或SDK启动和管理。Temporal简化了分布式系统的可靠性设计,适合电商、自动化任务等场景。

木头 638

YOLOv11改进 | 集成FEM、FFM与SCAM即插即用模块:特征增强、融合与空间上下文建模新范式

FEM解决特征表达不足问题,增强关键特征响应FFM优化多尺度融合,提升小目标检测精度SCAM建模空间上下文,改善目标交互预测在保持YOLOv11实时性优势的同时,实现mAP+8.1%的显著提升,特别在复杂光照、密集目标、交互预测场景表现突出。其模块化设计使开发者可根据需求灵活组合,为各种复杂场景提供定制化解决方案。未来,随着跨模态扩展与神经架构搜索的深入,YOLOv11-FSC将进一步推动目标检测技术向更智能、更高效的方向发展。

走向CTO的路上... 988

JDK8-2-流(2)- 流操作

上篇中简单介绍了什么是流以及使用流的好处,本篇主要介绍流的操作类型以及如何操作。

sql2008help的博客 413

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

Java JDK1.8 核心特性详解------Stream(流)的使用

在前面的章节(Java JDK1.8 核心特性详解------Stream(流)的基本介绍),我讲述了流的基本介绍,包括流的一些特性以及简单的用法。在下面这篇文章里,我们会更加具体的学习如何使用Stream对数据进行筛选、映射、查找、匹配、归约等基本功能,以及如何用新的方式创建流。 目录 流的基本使用 筛选 映射 查找和匹配 数值流 构建流 流的基本使用 下面这个List是后面...

苏暮沉觞的博客 1738

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

JDK1.8常见的Stream流

java8之后,新增的Stream,配合同版本出现的Lambda,给我们操作集合(Collection)提供了极大的便利。那么,stream流是什么?Stream将要处理的元素集合看作一种流,在流的过程中,借助Stream API对流中的元素进行操作,比如:筛选、排序、聚合等。中间操作,每次返回一个新的流,可以有多个。终端操作,每个流只能进行一次终端操作,终端操作结束后流无法再次使用。终端操作会产生一个新的集合或值。另外,stream不存储数据,而是按照特定的规则对数据进行计算,一般会输出结果。

TFHoney的博客 579

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

JDK1.8 list集合的一些操作

JDK1.8 list集合的一些操作 list排序: list.sort(Comparator.comparing(String::valueOf)); userList.sort(Comparator.comparing(user::getUserName));// (对象:属性) Comparator.comparing的默认排序是正序排列,要想倒序:.reversed() list.s...

客官你来啦,姑娘任你挑 3389

Stream集合详细操作(jdk1.8新特性)

Stream集合

qq_53464269的博客 3802

JDK8 新特性之Stream流方法详解

JDK8 新特性之Stream流方法详解

qq_61313896的博客 1958

常见的jdk8流操作总结

流操作可以分为两类:中间操作和终端操作  中间操作:又可以分为有状态和无状态两种 定义:一个流可以后面跟随零个或多个中间操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),就是说,仅仅调用到这类方法,并没有真正开始流的遍历。  终端操作:又可以分为短路和非短路两种 定义: 一个流只能有一...

qq_27661881的博客 619

JDK8 Stream流操作指南

JDK8 Stream API 摘要 Stream API是JDK8提供的函数式编程工具,支持链式操作集合和数组。使用分为三步:1)获取Stream流对象(集合.stream()/Arrays.stream());2)中间操作(filter过滤、sorted排序、limit限制等);3)终止操作(forEach遍历、count统计、collect收集等)。典型应用包括:过滤数据(filter)、排序(sorted)、去重(distinct)、映射转换(map)以及合并流(concat)。

m0_74529194的博客 429

JDK8 Stream流操作全解析

摘要:JDK8的Stream流通过链式操作简化集合/数组处理,支持获取流(集合/数组/零散数据)、中间操作(filter/map等)和终结操作(forEach/count)。核心特性包括延迟执行、一次性使用、支持收集结果(collect)和并行处理。开发中建议简单遍历用for循环,复杂操作优先Stream,大数据量可结合并行流提升效率。注意distinct需重写hashCode/equals,基本类型数组有专用流(IntStream等)。典型应用场景为多步数据筛选转换,如过滤特定条件的字符串并收集到新集合。

2601_96029214的博客 368

JDK8之Stream流

Stream流式思想类似于工厂车间的“生产流水线”,Stream流不是一种数据结构,不保存数据,而是对数据进行加工 处理。

bluede2015的博客 1256
上一篇: JDK8-2-流(1)-简介
下一篇: JDK8-2-流(2.2)- 流操作-flatMap
sql2008help
博客等级 码龄11年 80粉丝 145原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值