如何高效找出两个集合中的不同元素

从两个集合里面找出不同的元素不难,但是每个写法及实现的性能不一样,数量小的时候耗时差别不大,但数据量大的时候差别就十分明显了,如何解决优化?这就是算法的重要性了。本文以两个非空且自身无重复元素的两个集合举例。

方法一:使用hashMap

  /**
     * 找出两个list的不同元素,使用hashMap高效特性,数据量越大性能优势体现越为明显
     * @param list1 
     * @param list2
     * @return
     */
    public static List<String> getDiffByHashMap(List<String> list1,List<String> list2){
        long start = System.currentTimeMillis();
        List<String> resultList = new ArrayList<String>();
        //为了防止map扩容增加性能代价;初始化长度为两个链表的长度
        int capacity = (int)((float)(list1.size()+list2.size())/0.75F+1.0F);
        Map<String,Integer> map = new HashMap<>(capacity);
        for (String s:list1) {
            map.put(s,1);
        }
        for (String s:list2) {
            if(map.get(s) == null){
                map.put(s,1);
            }else {
                map.put(s,map.get(s)+1);
            }
        }
        for (Map.Entry m :map.entrySet()) {
            //value 大于1说明元素是两个list共有
            if((int)m.getValue() == 1){
                resultList.add((String)m.getKey());
            }
        }
        long end = System.currentTimeMillis();
        System.out.println("getDiffByHashMap耗时:"+(end-start));
        return resultList;
    }


方法二:使用HashSet



    /**
     * 找出两个list的不同元素,使用HashSet,数据量越大性能优势体现越为明显,亲测和使用hashmap时不分上下,甚至更快
     * @param list1
     * @param list2
     * @return
     */
    private static List<String> getDiffByHashSet(List<String> list1,List<String> list2){
        long start = System.currentTimeMillis();
        //temp存放两个list的共有元素
        List<String> temp = new ArrayList<>();
        //hashSet底层通过hashmpa实现的
        Set<String> set = new HashSet<>((int)((float)(list1.size()+list2.size())/0.75F+1.0F));
        for (String s1:list1) {
            set.add(s1);
        }
        for (String s2:list2) {
            //添加不成功说明元素共有
            if(!set.add(s2)){
                temp.add(s2);
            }
            set.add(s2);
        }
        //删除set中存在temp的元素
        set.removeAll(temp);
        long end = System.currentTimeMillis();
        System.out.println("getDiffByHashSet耗时:"+(end-start));
        return new ArrayList<>(set);
    }


方法三:使用集合原生方法和遍历

  /**
     * 找出两个list的不同元素,使用原生方法,数据量越大性能越低下
     * @param list1
     * @param list2
     * @return
     */
    private static List<String> getDiffByNative1(List<String> list1,List<String> list2){
    	long start = System.currentTimeMillis();
        List<String> resultList = new ArrayList<>();
        for (String s1 :list1) {
            if(!list2.contains(s1)){
                resultList.add(s1);
            }
        }
        for (String s2:list2) {
            if(!list1.contains(s2)){
                resultList.add(s2);
            }
        }
        long end = System.currentTimeMillis();
        System.out.println("getDiffByNative1耗时:"+(end-start));
        return resultList;
    }

方法四:使用集合的removeAll方法


 /**
     * 找出两个list的不同元素,使用原生方法removeAll,数据量越大越慢
     * @param list1
     * @param list2
     * @return
     */
    private static List<String> getDiffByNative2(List<String> list1,List<String> list2){
        long start = System.currentTimeMillis();
        List<String> resultList = new ArrayList<>();
        //temp临时存放其中一个list的值
        List<String> temp = new ArrayList<>();
        temp.addAll(list1);
        //删除list1存在list2的元素
        list1.removeAll(list2);
        //删除list2存在list的元素
        list2.removeAll(temp);
        resultList.addAll(list1);
        resultList.addAll(list2);
        long end = System.currentTimeMillis();
        System.out.println("getDiffByNative2耗时:"+(end-start));
        return resultList;
    }

最后测试一下

随机生成字符串RandomUtils工具类

package com.example.algorithm.list;
import java.util.Random;


/**
 * Copyright (C), 2018-2020
 * FileName: AliPayConstant
 * Date:     2020-08-07 17:18
 * Description: 随机数工具类
 */
public class RandomUtils {

    private RandomUtils() {
        throw new RuntimeException("new RandomUtil instance error");
    }

    /**
     * 获取随机验证码
     * @param codeArray:随机验证码数组
     * @param length:随机验证码长度
     */
    public static String getRandomCode(char[] codeArray, int length) {
        StringBuilder code = new StringBuilder();
        Random random = new Random();
        for (int i = 0; i < length; i++) {
            //random.nextInt该方法的作用是生成一个随机的int值,该值介于[0,n)的区间,也就是0到n之间的随机int值,包含0而不包含n。
            code.append(codeArray[random.nextInt(codeArray.length)]);
        }
        return code.toString();
    }

    /**
     * 生成指定长度的纯字母验证码
     * @param length 验证码长度
     */
    public static String getRandomWordCode(int length) {
        char[] codeArray = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
                's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
                'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };
        return getRandomCode(codeArray, length);
    }

}



开始测试,往集合中放入10000个字符串



  /**
     * 随机生成长度为10000,元素类型为String类型,字符串长度为5的Set
     * @param set
     */
    private static void addForList(Set<String> set){
        for(int i = 0; i < 10000; i++){
            set.add(RandomUtils.getRandomWordCode(5));
        }
    }



public static void main(String[] args) {

        Set<String> set1 = new HashSet<>();
        Set<String> set2 = new HashSet<>();
        //为测试结果更明显,手动创造两个列表有重复元素
        set1.add("ff");set2.add("ff");
        addForList(set1);addForList(set2);
        List<String> list1 = new ArrayList<>(set1);
        List<String> list2 = new ArrayList<>(set2);
        System.out.println("-----------------");
        System.out.println("list1="+list1);
        System.out.println("list2="+list2);
        System.out.println("list长度:"+list1.size()+";"+"list2长度:"+list2.size());
        List<String> result1 = getDiffByHashMap(list1,list2);
        List<String> result2 = getDiffByHashSet(list1,list2);
        List<String> result3 = getDiffByNative1(list1,list2);
        List<String> result4 = getDiffByNative2(list1,list2);
        System.out.println("getDiffByHashMap结果集长度:"+result1.size());
        System.out.println("getDiffByHashSet结果集长度:"+result2.size());
        System.out.println("getDiffByNative1结果集长度:"+result3.size());
        System.out.println("getDiffByNative2结果集长度:"+result4.size());
        System.out.println("-----------------");
        System.out.println("getDiffByHashMap结果集"+result1);
        System.out.println("getDiffByHashSet结果集"+result2);
        System.out.println("getDiffByNative1结果集"+result3);
        System.out.println("getDiffByNative2结果集"+result4);
    }


测试结果

在这里插入图片描述

总结:经过多次测试发现,数据量越大的情况下,他们的性能从好到差的排列是:方法2>方法1>方法4>方法3;若数据量过少则差别很小。

  • 7
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值