Java大集合求交集的方法比较

两个List集合求交集想必学过Java的都知道用系统自带的retainAll()方法,但是在数据量比较大时,这个方法效率并不高,利用空余时间研究了几种数据量较大时求两个集合交集的办法。本文主要研究了JDK自带方法求交集、Guava集合求交集、Java8的parallelStream并行流求交集、双指针方法求交集以及bitmap求交集的方法和效率。

JDK自带方法

最常用的求交集方法,在小数据量的时候没什么问题,一旦两个集合的数据量达到几十万级别时,效率就严重偏低,底层实际上也是两个for循环,只不过JDK做了一些相应优化,不是O(n^2)的双重for循环,感兴趣的同学可以阅读相应源码。

Guava集合工具类

Guava是谷歌出的一个工具类,里面包含了很多实用的方法,求交集的方法为Sets.intersection(list, list2)实际测试下来相当高效。

Java8并行流

parallelStream()借用了Java7的Fork/Join框架,采用分治+多线程的思想来求交集

双指针法

双指针法又称拉链法,就是先将两个集合排序,然后借用了二路归并排序的思想,利用两个指针分别在两个集合里面做标记,比较大小然后滑动,最后得到结果。

BitMap方法

将数据存进两个bitMap中,然后进行与操作,得到最终结果,属于一种空间换时间的方法,BitMap思想在海量数据处理中有很多妙用。

下面贴上具体实现的代码:

package com.test.spring.learn.retainall;

import com.google.common.collect.Sets;
import org.apache.commons.lang3.StringUtils;

import java.io.*;
import java.util.*;
import java.util.stream.Stream;

import static java.util.stream.Collectors.toList;

/**
 * Created by GeekBoy on 2020/1/4.
 */
public class RetainAllTest {
    public static void main(String[] args) {
        retainAllByGuava();
        retainAllByBitSet();
        retainAllByTwoPoint();
        retainAllByStream();
        retainAllByJDK();

    }
    /**
     * 用JDK方法求交集
     */
    private static void retainAllByJDK() {
        List<Integer> txtList = getIntegerList("e:\\a.txt");
        List<Integer> txtList2 = getIntegerList("e:\\b.txt");
        long begin = System.currentTimeMillis();
        txtList.retainAll(txtList2);
        long end = System.currentTimeMillis();
        System.out.println("JDK方法耗时:" + (end - begin));
        System.out.println("交集的个数为:" + txtList.size());
    }

    /**
     * 利用guava集合求交集
     */
    private static void retainAllByGuava() {
        List<Integer> txtList = getIntegerList("e:\\a.txt");
        List<Integer> txtList2 = getIntegerList("e:\\b.txt");
        long begin = System.currentTimeMillis();
        Set<Integer> list = new HashSet<>(txtList);
        Set<Integer> list2 = new HashSet<>(txtList2);
        Sets.SetView<Integer> intersection = Sets.intersection(list, list2);
        long end = System.currentTimeMillis();
        System.out.println("guava方法耗时:" + (end - begin));
        System.out.println("交集的个数为:" + intersection.size());
    }

    /**
     * java8 stream流求交集,实质上底层是用的多线程fork/join框架
     */
    private static void retainAllByStream() {
        List<Integer> txtList = getIntegerList("e:\\a.txt");
        List<Integer> txtList2 = getIntegerList("e:\\b.txt");
        long begin = System.currentTimeMillis();
        long count = txtList.parallelStream().
                filter(item -> txtList2.contains(item)).count();
        long end = System.currentTimeMillis();
        System.out.println("stream流求交集方法耗时:" + (end - begin));
        System.out.println("交集的个数为:" + count);
    }

    /**
     * 双指针法求两个list的交集
     */
    private static void retainAllByTwoPoint() {
        List<Integer> txtList = getIntegerList("e:\\a.txt");
        List<Integer> txtList2 = getIntegerList("e:\\b.txt");
        long begin = System.currentTimeMillis();
        Collections.sort(txtList);
        Collections.sort(txtList2);
        int count = 0;
        int m = 0;
        int n = 0;
        int length = txtList.size() + txtList2.size();
        for (int i = 0; i < length; i++) {
            if (m < txtList.size() && n < txtList2.size()) {
                if (txtList.get(m).equals(txtList2.get(n))) {
                    count++;
                    m++;
                    n++;
                } else if (txtList.get(m).compareTo(txtList2.get(n)) > 0) {
                    n++;
                } else {
                    m++;
                }
            } else if (m < txtList.size()) {
                if (txtList.get(m).equals(txtList2.get(n - 1))) {
                    count++;
                }
                m++;

            } else if (n < txtList2.size()) {
                if (txtList.get(m - 1).equals(txtList2.get(n))) {
                    count++;
                }
                n++;
            }
        }
        long end = System.currentTimeMillis();
        System.out.println("双指针方法耗时:" + (end - begin));
        System.out.println("交集的个数为:" + count);
    }

    /**
     * 利用bitmap求两个list的交集
     */
    private static void retainAllByBitSet() {
        List<Integer> txtList = getIntegerList("e:\\a.txt");
        List<Integer> txtList2 = getIntegerList("e:\\b.txt");
        long begin = System.currentTimeMillis();
        BitSet bitSet = new BitSet(Collections.max(txtList));
        BitSet bitSet1 = new BitSet(Collections.max(txtList2));
        for (int i = 0; i < txtList.size(); i++) {
            bitSet.set(txtList.get(i));
        }
        for (int i = 0; i < txtList2.size(); i++) {
            bitSet1.set(txtList2.get(i));
        }
        bitSet.and(bitSet1);
        long end = System.currentTimeMillis();
        System.out.println("bitSet方法耗时:" + (end - begin));
        System.out.println("交集的个数为:" + bitSet.cardinality());
    }

    /**
     * 从文件读取两个list<Integer>
     *
     * @param filePath
     * @return
     */
    private static List<Integer> getIntegerList(String filePath) {
        InputStream inputStream = null;
        InputStreamReader is = null;
        BufferedReader br = null;
        Set<Integer> txtList = new HashSet<>();
        try {
            File txtFile = new File(filePath);
            if (txtFile.exists()) {
                inputStream = new FileInputStream(txtFile);
                is = new InputStreamReader(inputStream, "UTF-8");
                br = new BufferedReader(is);
                String str = null;
                while ((str = br.readLine()) != null) {
                    if (StringUtils.isNotBlank(str)) {
                        txtList.add(Integer.valueOf(str));
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
                if (is != null) {
                    is.close();
                }
                if (br != null) {
                    br.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return new ArrayList<>(txtList);
    }
}

最终的运行结果如下,只运行了一次,结果并不严谨,仅供参考:

guava方法耗时:33
交集的个数为:151695
bitSet方法耗时:25
交集的个数为:151695
双指针方法耗时:63
交集的个数为:151695
stream流求交集方法耗时:28240
交集的个数为:151695
JDK方法耗时:91838
交集的个数为:151695

从上面的结果可以看出bieSet是最快的,guava的方法其次,JDK自带的最慢。平时使用如果数据量不是太大用guava的工具类即可,不得不说谷歌的算法还是相当厉害的。

参考链接

https://blog.csdn.net/banpeng4018/article/details/101386744

镜像地址

http://www.zhangwei.wiki/#/posts/14

原文地址:https://www.cnblogs.com/coderzhw/p/12150677.html

时间: 2024-08-04 21:18:42

Java大集合求交集的方法比较的相关文章

list1与list2求交集的方法总结!

一.有序集合求交集的方法有 a)二重for循环法,时间复杂度O(n*n) b)拉链法,时间复杂度O(n) c)水平分桶,多线程并行 d)bitmap,大大提高运算并行度,时间复杂度O(n) e)跳表,时间复杂度为O(log(n)) 以下是方法的具体介绍: 方案一:for * for,土办法,时间复杂度O(n*n) 每个搜索词命中的网页是很多的,O(n*n)的复杂度是明显不能接受的.倒排索引是在创建之初可以进行排序预处理,问题转化成两个有序的list求交集,就方便多了. 方案二:有序list求交集

JAVA大集合数据分批次进行切割处理

今天遇到一个大集合里面的数据删除问题, 因为是一个大集合,如果同时传递到数据库,那么就会造成数据库压力 所以分批次的进行批量操作 其实 也可以采用多线程来处理或者多批次加多线程来处理都是可以的 下面的案例 主要讲述是大集合拆分成小集合的代码 避免下次用到忘记了! public static void main(String[] args) { List<String> list=new ArrayList<String>(); for (int i = 0; i <=1000

oracle 和java 中求交集的方法

<if test="null != protocolEndTime and '' != protocolEndTime"> <![CDATA[ and( (#{protocolStartTime} >= t.protocol_start_time AND #{protocolStartTime} <= t.protocol_end_time) OR (#{protocolStartTime} <= t.protocol_start_time AND

Java遍历集合的几种方法分析(实现原理、算法性能、适用场合)

概述 Java语言中,提供了一套数据集合框架,其中定义了一些诸如List.Set等抽象数据类型,每个抽象数据类型的各个具体实现,底层又采用了不同的实现方式,比如ArrayList和LinkedList. 除此之外,Java对于数据集合的遍历,也提供了几种不同的方式.开发人员必须要清楚的明白每一种遍历方式的特点.适用场合.以及在不同底层实现上的表现.下面就详细分析一下这一块内容. 数据元素是怎样在内存中存放的? 数据元素在内存中,主要有2种存储方式: 1.顺序存储,Random Access(Di

java的集合操作(交集、并集、差集)整理

/**     * 求ls对ls2的差集,即ls中有,但ls2中没有的     *      * @param ls     * @param ls2     * @return      */    public static List diff(List ls, List ls2) {        List list = new ArrayList(Arrays.asList(new Object[ls.size()]));        Collections.copy(list, ls

Java遍历集合的几种方法

遍历集合的几种方法 用不同的方法遍历集合. public interface Iterator:对Collection进行迭代的迭代器.迭代器取代了Java Collections FrameWork中的Enumeration import java.util.ArrayList; import java.util.Collection; import java.util.Enumeration; import java.util.Iterator; import java.util.List;

Java Collection集合中的iterator方法

Iterator接口的概述 /** * java.util.Iterator接口:选代器(对集合进行遍历) * 有两个常用的方法 * boolean hasNext() * 如果仍有元素可以迭代,则返回true. * 即判断集合中还有没有下ー个元素,有就返回true,没有就返回 false * E next() * 返回送代的下一个元素. * 即取出集合中的下一个元素 * iterator迭代器,是一个接口,我们无法直接使用,需要使用Iterator接口的实现类对象. * 获取实现类的方式比较特

AutoCAD.Net/C#.Net QQ群:193522571 LinQ 两个集合求交集、并集

//取得交集 Pn = PnFace.Intersect(PnType).ToList(); //取得并集 Pn = PnFace.Concat(PnType).ToList();

Java中集合概念

1.集合是储存对象的,长度可变,可以封装不同的对象 2.迭代器: 其实就是取出元素的方式(只能判断,取出,移除,无法增加) 就是把取出方式定义在集合内部,这样取出方式就可以直接访问集合内部的元素,那么取出方式就被定义成了内部类. 二每一个容器的数据结构不同,所以取出的动作细节也不一样.但是都有共性内容判断和取出,那么可以将共性提取,这些内部类都符合一个规则Iterator Iterator it = list.iterator(); while(it.hasNext()){ System.out