【问题标题】:optimization: double loop through a set in java优化:通过java中的集合进行双循环
【发布时间】:2013-01-20 06:32:29
【问题描述】:

这段代码需要 9 分钟才能运行一组 5,600 个对象:

public Set<UnDirectedPair<T>> getAllUndirectedPairs(Set<T> setObjects) {
    Set<T> setObjectsProcessed = new TreeSet();
    Set<UnDirectedPair<T>> setPairs;
    setPairs = new TreeSet();
    Iterator<T> setObjectsIteratorA = setObjects.iterator();
    Iterator<T> setObjectsIteratorB;
    T currTA;
    T currTB;
    while (setObjectsIteratorA.hasNext()) {
        currTA = setObjectsIteratorA.next();
        setObjectsProcessed.add(currTA);
        setObjectsIteratorB = setObjects.iterator();
        while (setObjectsIteratorB.hasNext()) {
            currTB = setObjectsIteratorB.next();
            if (!setObjectsProcessed.contains(currTB) && !currTA.equals(currTB)) {
                setPairs.add(new UnDirectedPair(currTA, currTB));
            }
        }
        setObjectsProcessed.add(currTA);
    }
    return setPairs;

}

正在寻找一种方法来显着减少运行时间...想法?

[背景] 该集合包含人员。集合中有重复项(相同的人,但属性略有不同,因为输入时出现错误)。我有需要 2 人并进行必要更正的方法。因此,作为初步步骤,我需要创建一组 (Person, Person) 对,将其提供给这些方法。

【问题讨论】:

  • 你最好在codereview.stackexchange.com上提问
  • 你想用那个“双循环”做什么?
  • 在一个不相关的注释上:您可以使用 for (T t : setObjects) {} 而不是迭代器。因此,您可以跳过检查 hasNext() 和迭代器的创建。但我猜这不会带来明显的好处,只是可读性。
  • 你的方法 BigO(nnn){setObjectsProcessed.contains(currTB)} 这很糟糕。你能说一下你想要达到的目标吗?
  • collection.contains 将占用另一个 O(n)

标签: java loops optimization collections


【解决方案1】:

感谢您的好建议。

基本障碍是我的班级UnDirectedPair,它有昂贵的equalscompareTo 方法。我用剥离的裸 Pair 类替换它。 这使代码在大约 10 秒内运行。

不过,对集合使用操作似乎代价高昂。对@mawia 的建议稍作修改后,集合就可以完全排除在外。最终代码在不到 2 秒而不是 900 万 40 秒内运行 - 返回一个包含 19,471,920 个 Pair 对象的列表!!

public List<Pair<T>> getAllUndirectedPairsAsList(Set<T> setObjects) {
    List<T> listObjects = new ArrayList();
    listObjects.addAll(setObjects);

    List<Pair<T>> listPairs = new ArrayList();
    Iterator<T> listIterator1 = listObjects.listIterator();
    Iterator<T> listIterator2;
    int count = 1;
    T object1;
    while (listIterator1.hasNext()) {
        object1 = listIterator1.next();
        listIterator2 = listObjects.listIterator(count++);
        while (listIterator2.hasNext()) {
            listPairs.add(new Pair(object1, listIterator2.next()));
        }
    }
    return listPairs;
}

【讨论】:

    【解决方案2】:

    我建议的一个技巧是保持外循环和内循环的计数器。

    int outerCount=0;
    while (setObjectsIteratorA.hasNext()) {
        currTA = setObjectsIteratorA.next();
        setObjectsProcessed.add(currTA);
        setObjectsIteratorB = setObjects.iterator();
        int innerCount=0;
        while (setObjectsIteratorB.hasNext()) {
            currTB = setObjectsIteratorB.next();
            if (innerCount++>outerCount && !currTA.equals(currTB)) {
                setPairs.add(new UnDirectedPair(currTA, currTB));
            }
        }
     outerCount++;
        setObjectsProcessed.add(currTA);
    }
    return setPairs;
    

    这将保存最后一次包含一个 logN 的操作。

    背后的逻辑是:由于两个Iterator在同一个集合上,而ObjectProcessedSet的唯一目的是维护处理过的Object的记录,可以实现相同的比较索引。

    例子

      Set1={1,1,2,4,5}
      Iterator1 iteratorOuter=Set1.Iterator();
    
    
      int outerCount=0;
      while(iteratorOuter.hasNext()){
               Iterator2 iteratorInner=Set1.Iterator();
               int currA=iteratorOuter.next();
          while(iteratorInner.hasNext()){
               int CurrB=iteratorInner.next();
               //Now here if CurraA=4 and CurrB=2 it is obvious it has been processed
              //If currB =5 it is obviously has not been processed.
         }
      }
    

    【讨论】:

      【解决方案3】:

      一个应该给你一个很好的加速的解决方案是首先对集合进行排序,然后只比较集合中的相邻条目。

      当然,这意味着您需要为每个 Person 提供一个可比较的键(例如,它的名称),并且该键对于所有重复项都必须相同。

      那么您的代码可能如下所示:

      SortedSet<Person> persons = new TreeSet<>(...);
      Person last = null;
      for (Person current : persons) {
        if (last != null) {
          setPairs.add(new UnDirectedPair(last, current));
        }
        last = current;
      }
      

      如果Person 没有实现Comparable(或按错误字段进行比较),您可以在创建TreeSet 时指定Comparator

      这个解决方案在 O(n*log n) 中运行,之后您只有 O(n) 对可以处理。对于只有 5600 人,这应该很快。

      在这种情况下,您还可以将setPairs 设置为List 以获得更多性能(尽管很少)。或者你根本不创建这组对,而只是直接在循环中调用你的方法来更正Person 对象。

      【讨论】:

        猜你喜欢
        • 2018-11-13
        • 2018-12-16
        • 1970-01-01
        • 2017-12-26
        • 2018-10-09
        • 2015-07-24
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多