如何以一种好的方式在Java中的同一列表中查找对象对

在里面

我有一个包含不同对象的ArrayList 。 我试图在一个条件的基础上搜索相同的列表对象对。 如果我找到了正确的对,我创建一个新对象并将其添加到新列表中。 但是我想避免在objectA与objectB和objectB与objectA配对时创建一个对象对。

到目前为止,我没有找到一个好方法。

我试过的想法

2 for循环

 for(Object objectA : objectList){ for(Object objectB : objectList){ if(condition){ // create new object // add to list } } } 

问题:我需要对已匹配的对进行标记,否则将导致我想避免的同一对的两个创建对象。 它有效,但可能不是最好的解决方案?

迭代器

就像有两个forloops的版本一样,我使用了一个迭代器并从列表中删除了已匹配的对象对。 工作,但似乎不好?

Java8 forEach&removeIf

 objectList.stream().forEach(posA -> { objectList.removeIf(posB -> condition); }); 

问题:我什么时候创建对象对象…?

哪个是最好的主意 – 或者是否有更好的解决方案我没有得到?

显然你考虑的是无序对,其中对(a,b)与对(b,a)相同。 你必须自己为此目的创建一个类,例如

 class Pair { final T a, b; public Pair(T a, T b) { this.a = a; this.b = b; } @Override public boolean equals(Object obj) { if(obj==this) return true; if(!(obj instanceof Pair)) return false; Pair p=(Pair)obj; return Objects.equals(this.a, pa) && Objects.equals(this.b, pb) || Objects.equals(this.a, pb) && Objects.equals(this.b, pa); } @Override public int hashCode() { return Objects.hashCode(a) + Objects.hashCode(b); } } 

拥有一个带有所需语义的类,您可以简单地创建所有组合,并让Stream API删除重复项。 如果源列表已经重复,这甚至可以工作:

 List result = objectList.stream() .flatMap(objA -> objectList.stream().map(objB -> new Pair<>(objA,objB))) .distinct() .filter(pair -> condition) .map(pair -> new YourNewObjectType … ) .collect(Collectors.toList()); 

您没有指定是否允许元素与自身配对。 如果没有,您可以过滤掉这些情况:

 List result = objectList.stream() .flatMap(objA -> objectList.stream() .filter(objB -> !Objects.equals(objA, objB)) .map(objB -> new Pair<>(objA,objB))) .distinct() .filter(pair -> condition) .map(pair -> new YourNewObjectType … ) .collect(Collectors.toList()); 

作为旁注,如果结果类型的构造是无副作用且不昂贵且类型具有反映两个输入元素的相等,则可以考虑构造它们而不是Pair实例并为它们使用.distinct ,从而节省将Pair实例转换为YourNewObjectType实例。

如果您的源列表没有重复项,您可以利用这些知识根据索引构建唯一的对:

 List result = IntStream.range(0, objectList.size()) .mapToObj(i -> IntStream.range(i/*+1*/, objectList.size()) .mapToObj(j -> new Pair<>(objectList.get(i),objectList.get(j)))) .flatMap(Function.identity()) .filter(pair -> condition) .map(pair -> new YourNewObjectType … */) .collect(Collectors.toList()); 

如果不允许将元素与自身配对,只需将/*+ 1*/注释转换为实数+1 。 此代码的可读性较差,但可能更有效。

正如@AndyTurner所说,我认为这是objectList实现ArrayList的最佳方式:

  boolean[] removed = new boolean[objectList.size()]; Arrays.fill(removed, false); // unnecessary List result = new ArrayList<>(); for (int i = 0; i < objectList.size(); i++) { if (!removed[i]) { Object objectA = objectList.get(i); for (int j = i + 1; j < objectList.size(); j++) { if (!removed[j]) { Object objectB = objectList.get(j); if (condition) { removed[j] = true; result.add(new Pair(objectA, objectB)); break; } } } } } 

为什么非function性方法不被接受?

 List> pairs = new ArrayList<>(); for (int i = 0; i < objectList.size() - 1; i++) { for (int j = i+1; j < objectList.size(); j++) { if (condition) { pairs.add(new Pair<>(objectList.get(i), objectList.get(j))); } } } 

如果可以订购列表,另一种方法可能是:

  • 对列表进行排序

环:

  • 使用迭代器迭代,删除扫描的对象

  • 启动二进制搜索,找到与刚删除的元素配对的元素

  • 删除该元素并使用这两个元素创建自定义对象

这样,您可以将研究的时间复杂度从O(N ^ 2)减少到O(nlogn)。