【问题标题】:Fast way to iterate through a set and remove items meeting certain criteria快速迭代一组并删除满足特定条件的项目
【发布时间】:2013-01-11 19:27:37
【问题描述】:

所以我有一个包含一组记录的程序。该集合可能有几件或数十万件。每条记录的一位数据是时间戳。我需要消除一组中的所有项目,但其中一个在 15 秒内。最有效的方法是什么?

目前我创建了该集合的副本。然后我遍历集合,将第一个项目与其他所有项目进行比较,然后重复。如果在 15 秒内找到匹配项,我会将其从重复集中删除。然后将复制集写出到一个文件中。

显然这是可行的,但我终于意识到这是非常低效的。对于大型集,这似乎需要很长时间,假设它没有发生其他问题。有人可以为我提供一种更智能、更快、更有效(或只是适当)的方式来用 Java 执行此操作吗?我意识到,因为记录包含时间戳,所以对它们进行排序可能会有很大帮助。我想把这一切都包含在程序中,所以我想我需要研究排序和比较器。

我只是无法完全解决这个问题。我想出了一些其他的想法来改进我的代码,但我不禁觉得我仍然完全错误。感谢您的任何建议。

哦,这是给工作的,不是学校的或其他任何东西,因此感谢您的帮助。

【问题讨论】:

  • 当然 - 对列表进行排序 - 然后你就可以通过一次。
  • 你为什么不构建一个Map,将时间戳作为键,将匹配项列表作为值?
  • 另外,这个“第一个元素”是什么?你使用 LinkedHashSet 吗?
  • @fge 以时间戳为键的地图对我完全没有帮助,它对第一个元素有什么影响?不,我现在使用 TreeSet 进行排序。
  • @cardician 看到我的回答:拥有地图会有很大帮助。

标签: java performance data-structures collections


【解决方案1】:

现在,您描述的算法在 O(n2) 时间内运行。

现在,如果您需要更快的算法,您可以做的是

  • 对您的集合进行排序(如果 java 没有基类排序函数,我会感到惊讶)O(n * lg(n))
  • 15 秒内的所有“匹配”都将紧挨着彼此
  • 您只需要遍历每个元素一次只检查相邻元素O(n)

如果你这样做,那么你的算法可能会更易于管理O(n * lg(n))时间复杂度

Here's some information regarding Java's Array.sort()

【讨论】:

  • 我很欣赏有关排序时间的信息,但我需要更多信息。我不知道如何,即使在排序列表上搜索,它也快得多。当然,一旦我移出 15 秒窗口,我就可以跳出循环,但是对于下一个项目,我必须遍历整个列表,直到我到达它在列表中的位置,然后从那里搜索。我不知道如何在不通过列表进行比较的情况下做到这一点,据我所知,这并不能节省我任何时间。我知道我只是不明白。
  • @cardician 无需将每个项目与其他每个项目进行比较,您只需将其与紧接在之前紧接在之后的项目进行比较即可知道是否距离另一个项目 15 秒。这为您节省了很多比较
  • 谢谢。通过您的回答以及来自 lbalazscs 的更多信息,我获得了足够的理解来正确实施事情。现在它比我的方法快得多了,可惜我的编码太糟糕了。
【解决方案2】:

您可以继续使用 Set,只需确保从一开始就对其进行排序,例如 TreeSet(或 ConcurrentSkipListSet,如果您有多个线程)。要么你实现 Comparable 以便比较时间戳,要么你提供一个 Comparator 来做同样的事情。

这将保证您不能有重复项(就像您之前那样),并且还可以简化您的代码。插入 TreeSet 也会花费你 O(n log n) 的时间。

从这里开始,您可以继续使用 Sam I am 建议的方法:迭代器将按元素升序遍历它,您只需将每个元素与前一个元素和下一个元素进行比较。

顺便说一句,您不需要将所有内容复制到另一个 Set,只需确保使用迭代器的 remove 方法,而不是 TreeSet 的 remove:Iterating through a Collection, avoiding ConcurrentModificationException when removing in loop

【讨论】:

  • 非常感谢您。您帮助我让事情变得更清晰,我现在能够以这种方式正确实施它。它比我的方法快得令人难以置信。
【解决方案3】:

如果你有地图,说:

Map<Long, List<MyClass>> map;

其中键是时间戳,那么你可以这样做:

// Value of wanted elements
List<MyClass> ret = new ArrayList<MyClass>();

// Go over all timestamps: if a timestamp is wanted, add all
// corresponding elements
for (Map.Entry<Long, List<MyClass>> entry: map.entrySet())
    if (wanted(entry.getKey()))
        ret.addAll(entry.getValue());

// Return
return ret;

【讨论】:

    【解决方案4】:

    我还没有测试过性能,但我可能实现的一种方法是创建一个 Set 并覆盖相关对象类型的 equals() 方法。

    public boolean equals( Object o )
    {
      return( Math.abs( this.getTimestampSeconds() - o.getTimestampSeconds() ) < 15 );
    }
    

    通过这样做,当您将每一行添加到集合中时,对于任何给定的 15 秒时间片,您最终只会得到一个条目。

    * 编辑 **

    我不会对常规域对象执行此覆盖。我可能只会在某种外观对象中执行此操作——它是专门为此目的而创建的。

    另外,正如其他人所说。这假定您的输入列表按时间戳升序排序。

    【讨论】:

      猜你喜欢
      • 2020-10-06
      • 1970-01-01
      • 1970-01-01
      • 2014-06-12
      • 2022-11-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多