优化:通过java中的一个集合进行双循环

时间:2013-02-05 10:48:39

标签: java loops optimization collections

此代码需要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人并进行必要的更正。因此,作为一个初步步骤,我需要创建一组(人,人)对,这些对将被用于这些方法。

3 个答案:

答案 0 :(得分:1)

我建议的一个技巧是保持外循环和内循环的反击。

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.
     }
  }

答案 1 :(得分:0)

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

当然,这意味着您需要为每个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(或通过错误字段进行比较),则可以在创建Comparator时指定TreeSet

此解决方案在O(n * log n)中运行,之后只有O(n)对才能运行。只有5600人,这应该非常快。

在这种情况下,您还可以使setPairs成为List以获得更多性能(尽管很少)。或者你根本不创建对的集合,只需调用你的方法直接在循环中纠正Person个对象。

答案 2 :(得分:0)

感谢您提出好的建议。

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

尽管如此,使用套装操作似乎代价高昂。随着@mawia建议修改了一下,设置可以完全不在图片之外。最终代码在 2秒内而不是9月40日运行 - 返回19,471,920对对象列表!!

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;
}