【问题标题】:Remove duplicates from a large integer array using Java使用 Java 从大整数数组中删除重复项
【发布时间】:2011-04-09 17:07:57
【问题描述】:

您知道使用 Java 从非常大的整数数组中删除重复值的任何高效方法吗?数组的大小取决于登录的用户,但总是会超过 1500000 个未排序的值,其中有一些重复。每个整数都包含一个介于 100000 和 9999999 之间的数字。

我尝试将其转换为列表,但我的服务器上的堆不允许这样的数据量(我的 ISP 已限制它)。并且 for 循环中的常规 for 循环需要 5 分钟以上的时间来计算。

没有重复的数组的大小是我将存储在我的数据库中的那个。

我们将不胜感激!

【问题讨论】:

    标签: java arrays loops integer


    【解决方案1】:

    在开始向列表中添加项目之前,我会创建一个哈希集来存储列表中包含的所有值。然后只需检查以确保哈希集不包含您要添加的值。

    【讨论】:

    • “我尝试将其转换为列表,但我的服务器上的堆不允许这么多数据”——这可能也排除了 Set。
    • 在我看来,对于大型数据集,列表比哈希集更浪费内存。但我可能是错的。 =/
    • 这在很大程度上取决于列表的实现。我相信ArrayListHashSet 更节省内存,但我也可能错了:-)
    【解决方案2】:
    Set<Integer> set = new HashSet<Integer>();
    Collections.addAll(set, array);
    

    您只需要一个 Integer[] 数组,而不是 int[]

    【讨论】:

    • “我尝试将其转换为列表,但我的服务器上的堆不允许这么多数据”——这可能也排除了 Set。
    • 是的,这更重要。 @user435140 请注意,这仅在您的数组包含 Integer,而不是原始 int 时才有效。
    【解决方案3】:

    您可以先尝试对数组进行排序:

    int arr[] = yourarray;
    Arrays.sort(arr);
    // then iterate arr and remove duplicates
    

    【讨论】:

    • @Bozho 他可以迭代数组并计算唯一值。显然这是他唯一需要做的事情...没有重复的数组的大小是我将存储在我的数据库中的那个...
    • 通过先排序,然后您可以对数组进行最终遍历,并且只保留每个唯一值中的一个。这应该给出 O(n log n) 的复杂度,而不是提到的双循环的 O(n^2)。
    • 首先假设你有足够的资源来排序!
    • @Danny,Arrays.sort(...) 不使用更多空间:它“就地”排序。
    • @Bart K - 这取决于您的实现,但 JDK 不保证就地排序。许多人实际上使用了一种需要 O(n) 额外空间的合并排序。
    【解决方案4】:

    也许您可以对数据进行几次传递?例如,如果您对数据进行了十次传递,并将上面的一组建议应用于数据的较小子集(例如,当值 mod pass# == 0 时)。因此:

    for (int i = 0 to 9) {
      set = new Set()
      for (each entry in the data set) {
        if (entry % i == 0) {
          set.add(entry)
        }
      }
      output set
    }
    

    通过这种方式,您可以用时间换取内存(增加通过次数以减少内存/更多时间,反之亦然)。

    【讨论】:

      【解决方案5】:

      您也许可以使用位设置?我不知道 Java 的 BitSet 有多高效。但是 9999999 个可能的值只需要 9999999 / 8 = 1250000 字节 = 刚刚超过 1Mb。在遍历值数组时,将相应的位设置为 true。然后,您可以遍历该位集,并在发现某个位设置为真时输出相应的值。

      1Mb 将适合 CPU 缓存,因此这可能非常有效,具体取决于位集实现。

      这也有对数据进行排序的副作用。

      而且...这是一个 O(n) 算法,因为它需要对输入数据进行一次传递,集合操作是 O(1)(对于像这样的基于数组的集合),并且输出传递是也是 O(m),其中 m 是唯一值的数量,根据定义,必须是

      【讨论】:

      • 这样的聪明答案是我来 StackOverflow 的原因
      【解决方案6】:

      也许使用 primitives 而不是对象的哈希集可以完成这项工作?有免费的实现(以前没用过,但也许可以):

      http://trove4j.sourceforge.net/

      http://trove4j.sourceforge.net/javadocs/gnu/trove/TIntHashSet.html

      然后看起来像:

      int[] newArray = new TIntHashSet(yourArray).toArray();
      

      【讨论】:

        【解决方案7】:
        int[] a;
        Arrays.sort(a);
        int j = 0;
        for (int i = 1; i < a.length; ++i) {
          if (a[i] != a[j]) {
            ++j;
            a[j] = a[i];
          }
        }
        // now store the elements from 0 to j (inclusive - i think)
        

        【讨论】:

        • 如果不需要对结果进行排序,您可以从“开始”(复制时递增)复制值以减少副本数。 (每个副本一个,而不是每个元素一个)
        【解决方案8】:

        如果您确定整数具有合理的小值(例如,总是大于零且小于 1000 或 10000),您可以尝试这样的技巧:

            final int MAX = 100; 
            int[] arrayWithRepeats = {99, 0, 10, 99, 0, 11, 99};
        
            //we are counting here integers with the same value
            int [] arrayOfValues = new int[MAX+1];
            int countOfUniqueIntegers = 0;
            for(int i : arrayWithRepeats) {
                if(arrayOfValues[i] == 0) {
                    countOfUniqueIntegers++;
                }
                arrayOfValues[i]++;
            }
        
            // you can use arrayOfValues (smaller) or convert it
            // to table of unique values (more usable)
        
            int[] arrayOfUniqueValues = new int[countOfUniqueIntegers];
            int index = 0;
            for(int i = 0; i<arrayOfValues.length; i++) {
                if(arrayOfValues[i] != 0) {
                    arrayOfUniqueValues[index] = i;
                    index++;
                }
            }
        
            //and now arrayOfUniqueValues is even sorted
            System.out.println( Arrays.toString(arrayOfUniqueValues) );
        

        输出:[0, 10, 11, 99]

        【讨论】:

        • 这与我的位集建议基本相同,只是您使用的是每个条目的 32 位而不是 1,因此内存很快就会成为问题。此外,OP 表示该值将高达 9999999。
        • 由于“每个整数都包含一个介于 100000 和 9999999 之间的数字”,这将不起作用。
        • 你是对的。好主意是按照 Danny 的想法将 arrayOfValues 从 int[] 更改为 BitSet。
        【解决方案9】:

        真正绝望的人可以将数组写入磁盘并分叉sort | uniq | wc -l &lt;infile.txt 并捕获输出。如果内存仍然太紧或整数的域空间变大,则需要这样做。我不喜欢这样(他甚至在运行 unix!)但我的观点是有许多方法可以完成任务。

        另一个观察结果是最小值为 100,000。所以我们可以从最大值 9,999,999 中减去 100,000,从而减少域空间,从而节省一些内存。也许 100k/8 位在方案中只是小菜一碟,但它基本上是免费的。

        【讨论】:

          猜你喜欢
          • 2012-12-04
          • 2012-04-20
          • 2015-11-07
          • 2014-04-16
          • 2012-12-06
          • 2012-03-25
          • 2014-04-23
          • 1970-01-01
          • 2012-08-05
          相关资源
          最近更新 更多