ArrayList去重常用的四种方式及性能对比(JMH性能分析)

【Java必修课】ArrayList与HashSet的contains方法性能比较(JMH性能测试) 1 简介 在日常开发中,ArrayList和HashSet都是Java中很常用的集合类。 ArrayList是List接口最常用的实现类; HashSet则是保存唯一元素Set的实现。 本文主要对两者共有的方法contains()做一个简单的讨论,主要是性能上的对比,并用JMH(ava Microbenchmark Harness)进行测试比较。 2 先看JMH测试结果 我们使用一个由O... 阅读详情

这里提供四种实用的ArrayList去重的方法,同时我会实用Oracle提供的性能测试工具JMH(Java Microbenchmark Harness,Java微基准测试套件)来测试一下这几种方法的性能。当然这里测试的是在系统性能充裕的情况下进行的,看一看谁更能压榨机器的性能。

当然,那种不常用的去重方式这里就不涉及了。

ArrayList去重常用方法

ArrayList去重常用方法分为四种,分别是 HashSet去重、LinkedHashSet去重、stream流单线程去重、stream流多线程去重。这里涉及到的数据结构先回顾一下:

ArrayList(有序不唯一):Object[],线程不安全,插入删除慢(数组移动),查询快(随机访问)。

HashSet(无序唯一):哈希表,基于HashMap实现,hashmap的key存储它的值。线程不安全。

LinkedHashSet(有序唯一):哈希表+链表(链表存储插入顺序),它是HashSet子类,内部只有构造函数。线程不安全。

HashSet & LinkedHashSet

// data
List<Integer> arrayList = new ArrayList<Integer>(){{add(3);add(2);add(4);add(2);add(3);add(5);add(4);add(5);}};
// HashSet去重
ArrayList<Integer> hashSetList = new ArrayList<>(new HashSet<>(arrayList));
// LinkedHashSet去重
ArrayList<Integer> linkedHashSetList = new ArrayList<>(new LinkedHashSet<>(arrayList));

打上断点查看一下数据

在这里插入图片描述

stream & parallelStream

List<Integer> arrayList = new ArrayList<Integer>(){{add(3);add(2);add(4);add(2);add(3);add(5);add(4);add(5);}};
// stream去重
List<Integer> streamList = arrayList.stream().distinct().collect(Collectors.toList());
// parallelStream去重
List<Integer> parallelStreamList = arrayList.parallelStream().distinct().collect(Collectors.toList());

打上断点查看数据

在这里插入图片描述

上面就是简单数据的去重操作方法。

JMH性能分析

使用 Oracle 官方提供的性能测试工具 JMH(Java Microbenchmark Harness,JAVA 微基准测试套件)来测试一下这 4种 去重方式的性能。

首先,我们先要引入 JMH 框架,在 pom.xml 文件中添加如下配置:

<!-- https://mvnrepository.com/artifact/org.openjdk.jmh/jmh-core -->
<dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-core</artifactId>
    <version>1.23</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.openjdk.jmh/jmh-generator-annprocess -->
<dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-generator-annprocess</artifactId>
    <version>1.23</version>
    <scope>provided</scope>
</dependency>

版本号我以及写好了,推荐使用最新版本。

下面编写测试代码,如下

@BenchmarkMode(Mode.AverageTime) // 测试完成时间
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 2, time = 1, timeUnit = TimeUnit.SECONDS) // 预热 2 轮,每次 1s
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)//测试 5 轮,每次 1s
@Fork(1) // fork 1 个线程
@State(Scope.Thread) // 每个测试线程一个实例
public class ArrayListDistinctTest {
    static List<Integer> arrayList = new ArrayList<Integer>(){{
        // 一共 2000000 条数据
        for (int i = 0; i < 1000000; i++) add(i);
        for (int i = 0; i < 1000000; i++) add(i);
    }};

    public static void main(String[] args) throws RunnerException {
        // 启动基准测试
        Options opt = new OptionsBuilder()
                .include(ArrayListDistinctTest.class.getSimpleName()) // 要导入的测试类
                .output("C:/Users/86177/Desktop/jmh-map.log") // 输出测试结果的文件
                .build();
        new Runner(opt).run(); // 执行测试
    }
    @Benchmark
    public void hashSet(){
        new ArrayList<>(new HashSet<>(arrayList));
    }

    @Benchmark
    public void linkedHashSet(){
        new ArrayList<>(new LinkedHashSet<>(arrayList));
    }

    @Benchmark
    public void stream(){
        arrayList.stream().distinct().collect(Collectors.toList());
    }

    @Benchmark
    public void parallelStream(){
        arrayList.parallelStream().distinct().collect(Collectors.toList());
    }
}

所有被添加了 @Benchmark 注解的方法都会被测试,执行main方法即可。而正如代码中所示,我测试了一共 2000000(两百万) 条数据,有一半是重复的。已经把测试文件打印到桌面,我这里就贴上性能对比那一块数据:

Benchmark                             Mode  Cnt          Score          Error  Units
ArrayListDistinctTest.hashSet         avgt    5   37251270.300 ± 10458771.326  ns/op
ArrayListDistinctTest.linkedHashSet   avgt    5   40382889.634 ± 11785155.916  ns/op
ArrayListDistinctTest.parallelStream  avgt    5  157258934.286 ± 34670245.903  ns/op
ArrayListDistinctTest.stream          avgt    5   38513055.914 ±  3839780.617  ns/op

其中 Units 为 ns/op 意思是执行完成时间(单位为纳秒),而 Score 列为平均执行时间, ± 符号表示误差。从以上结果可以看出,两个 Set 的性能相近,并且执行速度很快,接下来是 stream 。

注:以上结果基于测试环境:JDK 1.8 / Win10 16GB (2021H) / Idea 2022.3

结论

除了以上数据量比较大的情况外,我还测试了进1000的小数据量。HashSet和LinkedHashSet的性能还是非常让人满意的,而对于stream来说在数据量比较小的情况下性能并没有Set高(parallelStream性能不如stream,下面有简单分析)。建议使用HashSet和LinkedHashSet去重。

去重选择

基于以上,有ArrayList去重操作的时候,推荐使用LinkedHashSet。

去重操作说到底但还是基于集合的数据来说的,你看Set集合都不允许重复,当然是首先选它了。然后就是看一下需求,如HashSet去重后数据无序,LinkedHashSet去重后数据有序但是性能没有HashSet好。

Stream流选择的话有单线程的和多线程的流操作,这里的多线程操作流性能是要优于单线程操作的,需要判断一下当前操作有没有可能发生并发风险等。

注意一点:在计算机体系中,多线程操作未必一定优于多线程,单线程可能会优于多线程的原因通常是CPU切换操作线程造成的性能浪费,这当中会有一个线程休眠和线程唤醒操作。比如常用的Redis就设计成单线程。

JMH - Java 微基准测试工具(自助性能测试)@Benchmark 1、前言 在日常开发中,我们对一些代码的调用或者工具的使用会存在多种选择方式,在不确定他们性能的时候,我们首先想要做的就是去测量它。大多数时候,我们会简单的采用多次计数的方式来测量,来看这个方法的总耗时。 但是,如果熟悉JVM类加载机制的话,应该知道JVM默认的执行模式是JIT编译与解释混合执行。JVM通过热点代码统计分析,识别高频方法的调用、循环体、公共模块等,基于JIT动态编译技术,会将热点代码转换成机器码,直接交给CPU执行。 也就是说,JVM会不断的进行编译优化,这就使得很难确定重复多少次 阅读详情

相关推荐

Java 集合框架应用对比:ArrayList vs LinkedList 在Truck类中的3种实现与性能分析

本文深入对比了Java集合框架中ArrayList与LinkedList在Truck类中的三种实现与性能优化实践。通过家电物流管理系统中的卡车装载计算场景,详细分析了两种集合的优缺点及适用场景,并提供了性能基准测试数据与工程实践建议,帮助开发者做出更优的技术选型。

weixin_31960565的博客 259

ArrayList去重的几种方法

ArrayList去重的几种方法

weixin_57869541的博客 9629

Java List去重性能优化:HashSet、LinkedHashSet与Stream API实战对比

在Java集合操作中,数据去重是一个基础且高频的需求,其核心原理是利用哈希表(Hash Table)或类似数据结构实现元素的唯一性判断。哈希表通过hashCode()和equals()方法快速定位元素,从而实现O(1)时间复杂度的查找,这是高效去重的技术基石。从技术价值看,合理的去重方案能有效提升程序性能、减少内存占用,并保障数据一致性。在实际应用场景中,开发者常需根据数据规模、顺序要求及运行环境进行选型。例如,对于大数据量处理,需警惕内存溢出(OutOfMemoryError)风险;而在高并发服务中,不当

weixin_30947043的博客 400

ArrayList去重

这是小编自己的一些学习总结

lz17267861157的博客 2440

用Matlab和CVX搞定原子范数最小化:一个信号处理小白的避坑实践

本文详细介绍了如何使用Matlab和CVX实现原子范数最小化(Atomic norm minimization),特别适合信号处理初学者。从CVX安装配置、核心代码解析到实战中的典型问题与解决方案,提供了全面的避坑指南。通过具体案例展示了原子范数在频率估计和DOA估计中的优势,并给出性能优化策略和可视化方法。

weixin_28731231的博客 232

【AI蛋白预测实战】零门槛在Colab上运行AlphaFold2全流程解析

本文为零基础用户提供了在谷歌Colab平台上运行AlphaFold2进行蛋白质结构预测的完整实战指南。通过ColabFold项目,用户无需本地高性能硬件,即可免费利用云端GPU完成从序列输入到3D结构生成和结果可视化的全流程,大幅降低了AI蛋白预测的技术门槛。

mqtt6iot的博客 912

List去除重复数据的五种方式

你知道的越多,不知道的就越多,业余的像一棵小草!你来,我们一起精进!你不来,我和你的竞争对手一起精进!编辑:业余草blog.youkuaiyun.com/qq_37939251/article/de...

xmt1139057136的专栏 7987

双轴按键摇杆模块原理与嵌入式驱动实战

双轴按键摇杆是一种融合模拟量(X/Y轴)与数字量(Z轴按键)的复合型人机交互传感器,其本质基于电位器分压原理与机械开关通断特性。通过ADC采集连续电压信号并结合数字IO检测按键状态,可实现高鲁棒性的二维运动感知与触发控制。该方案技术门槛低、成本可控、兼容性强,广泛应用于智能小车、DIY游戏手柄、工业HMI及教育开发板等嵌入式场景。文中深入解析电位器线性度、KEY引脚开漏特性、ADC参考电压稳定性及软件消抖状态机等关键工程要素,为基于Arduino、STM32或ESP32平台的快速集成提供可复用的技术路径。

weixin_30995917的博客 82

【Java基础篇】ArrayList集合去除重复元素的5种方式

在Java语言中,ArrayList集合的去重方式大致可以归纳总结为5种: 1. 双重for循环去重; 2. for循环去重; 3. 借助HashSet方式去重; 4. 借助LinkedHashSet去重; 5. 使用Java 8 新特性stream去重。 下面,就这5种方式进行简单的介绍: 双重for循环去重 这应该是 ...

weixin_43299268的博客 1万+

App inventor2通过蓝牙控制Arduino Uno LED开关

App inventer2控制Arduino Uno LED开关 如果学会这个小程序,大部分的手机通过蓝牙控制Arduino的功能都可以实现,这个小示例是通过蓝牙模块向Arduino发送"0"和"1",而其它功能要发送其它不同的字符串内容给Arduino. 所需硬件: Arduino Uno JDY-16 蓝牙模块(我的是蓝牙4.2版本) 草帽LED(可选) 手机(我用的是安卓,苹果手机应该也可以,最近其应用商店也有App inventor(AI)伴侣) 软件: Aduino IDE或者Platfom

Firmin123456的博客 5499

别再手动算临界转速了!用ANSYS Workbench 2023 R2快速搞定转子坎贝尔图(附完整APDL命令流)

本文详细介绍了如何利用ANSYS Workbench 2023 R2快速生成转子坎贝尔图,实现全自动临界转速分析。通过对比传统手动计算与Workbench自动化流程的效率差异,展示了其在旋转机械设计中的革命性优势,包括大幅缩短分析周期、提升精度至98.7%。文章还提供了完整的APDL命令流和实战案例,帮助工程师快速掌握这一高效工具。

weixin_42699679的博客 472

性能调优之JMH必知必会1:什么是JMH

性能调优之什么是JMH。

u014282578的博客 8894

Halcon频域滤波实战:5分钟搞定傅里叶变换与功率谱分析(附完整代码)

本文提供Halcon频域滤波的实战指南,重点讲解如何利用傅里叶变换与功率谱分析快速解决图像处理难题。通过核心算子fft_image和power_real,演示了从空间域转换到频域、分析功率谱以定位周期性噪声,并构造滤波器进行精准去除的完整流程,附有可直接运行的代码示例。

docker8compose的博客 215

Java--LinkedList真的比ArrayList添加元素快?Open JDK JMH带你揭开真相

【学习背景】 欢迎进来学习的小伙伴~ 不管你是学生,还是职场小白,还是入行1~3年的小伙伴,相信很多小伙伴在面试Java工作岗位时,发现LinkedList和ArrayList这个问题基本是必面的一道题, 【面试场景】 当面试官问到LinkedList和ArrayList的区别时,可能很多准备得不够充分的小伙伴第一反应的回答仅仅是这样的: LinkedList底层数据结构是链表,添加和删除元素效率比ArrayList高~ ArrayList底层数据结构是数组,查询效率比LinkedList高~ 面试官:哦

JustinQin 2万+

【YOLOv11改进- 主干网络】YOLOv11+RepViT: 从ViT的角度重新审视Mobile的CNN助力YOLOv11有效涨点;

本文给大家带来的改进内容是在YOLOv11中更换主干网络为RepViT,助力YOLOv11有效涨点,论文通过集成轻量级 ViT 的高效架构设计,逐步增强了标准轻量级 CNN(即 MobileNetV3)的移动友好性。这最终产生了一个新的纯轻量级 CNN 系列的主干网络。

qq_41304809的博客 786

【电源专题】案例:对比不同电池在不同电流下的电池放电曲线

无

一点硬件 2840

JMH实战:用ArrayList和LinkedList性能对比告诉你为什么选择很重要(附避坑指南)

本文通过JMH微基准测试对比了Java中ArrayList和LinkedList的性能差异,揭示了在不同操作场景下的选择策略。测试结果显示,ArrayList在随机访问和遍历操作中表现优异,而LinkedList在头部插入删除方面更具优势。文章还提供了实战选择指南和性能优化技巧,帮助开发者避免常见陷阱,提升应用性能。

wine的专栏 680

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

Java List去重性能对比:从HashSet到Stream,实战选型与避坑指南

在Java开发中,数据处理与集合操作是基础且高频的技术点,其中List去重是常见的性能优化场景。其核心原理在于利用哈希表(如HashSet)实现O(1)时间复杂度的查找,从而将整体算法复杂度从暴力循环的O(n²)优化至O(n)。这种优化对于提升系统吞吐量、降低资源消耗具有显著的工程价值,尤其在高并发、大数据量处理等场景下至关重要。实际应用中,开发者需根据是否保留元素顺序、数据规模及内存限制等因素,在HashSet、LinkedHashSet、Stream API distinct()等方法间做出权衡。本文基

weixin_33924770的博客 332

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

Java--LinkedList真的比ArrayList添加元素快?Open JDK JMH带你揭开真相_为什么arraylist添加元素比linklist快

*由于添加元素消耗的内存比较大,idea执行基准测试过程中可能会出现内存泄露的报错:**java.lang.OutOfMemoryError: Java heap space。

2401_86454343的博客 1378

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

List 三兄弟用法对比:ArrayList、LinkedList、Vector 的 10 个核心 API 速查(附 JMH 基准)

连续内存 + 随机读 → ArrayList头尾插入 + 低频随机读 → LinkedList老接口兼容 + 全表锁 → Vector(已弃用)并发读写 → 下篇讲下篇预告第3篇《Set 三兄弟用法对比:HashSet、LinkedHashSet、TreeSet 去重规则与迭代顺序》——带你手写 BloomFilter,看百万级去重内存省 90%!

持续输出Java相关知识 906

Java 17 泛型实战:3种 ArrayList 声明方式在大型项目中的性能与内存开销对比

本文深入对比了Java 17中三种ArrayList泛型声明方式(`new ArrayList<>()`、`new ArrayList<String>()`和`new ArrayList<Object>()`)在大型项目中的性能与内存开销。通过JMH基准测试和GC日志分析,揭示了显式类型声明与类型推断的性能差异,为高性能Java应用提供了优化建议。

400

Java PTA练习避坑指南:如何高效处理对象数组去重(附完整代码示例)

本文详细解析了Java对象数组去重的核心原理与实战技巧,重点介绍了正确实现equals方法的五个要点,并对比了三种去重方法的性能差异。针对PTA编程练习中的常见问题,提供了从基础实现到性能优化的完整解决方案,帮助开发者高效处理对象数组去重任务。

weixin_29214559的博客 359

JMH快速入门

JMH简介 JMH是专门用于代码微基准测试的工具集。JMH是由实现Java虚拟机的团队开发的,由于现在JVM已经变得越来越智能,在Java文件的编译阶段、类的加载阶段,以及运行阶段可能进行了不同程度的优化,因此开发者 ...

batman 837

【Java】ArrayList与LinkedList的性能对比深度解析

ArrayList与LinkedList的性能对比深度解析

啊松同学的博客 1323

Java List去重性能优化:从HashSet到Stream API的实战对比

在数据处理中,集合去重是常见的基础操作,其核心原理是利用数据结构的唯一性特性来消除重复元素。哈希表(HashSet)通过O(1)的平均时间复杂度实现高效去重,是性能最优的底层技术方案。Java 8引入的Stream API则提供了声明式的函数式编程范式,其distinct()操作在简化代码的同时,内部也基于集合实现去重。这些技术在处理数据库查询结果、日志分析或接口数据整合等场景中具有重要价值,能有效提升应用性能与代码可读性。本文聚焦于Java中List去重的多种实现方案,通过对比HashSet、Linked

cuanji3287的博客 547
上一篇: FastJSON解析Json字符串(反序列化为List、Map)
祈望每天自然醒
博客等级 码龄7年 1536粉丝 119原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值