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

时间:2016-09-06 09:16:25

标签: java list arraylist java-8

初始化

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

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

我试过的想法

2 for 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);
});

问题:我何时会创建对象对象...?

问题

哪个是最好的主意 - 还是有一个我没有得到的更好的解决方案?

4 个答案:

答案 0 :(得分:10)

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

class Pair<T> {
    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, p.a) && Objects.equals(this.b, p.b)
            || Objects.equals(this.a, p.b) && Objects.equals(this.b, p.a);
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(a) + Objects.hashCode(b);
    }
}

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

List<YourNewObjectType> 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<YourNewObjectType> 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<YourNewObjectType> 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即可。此代码可读性较差,但可能更有效。

答案 1 :(得分:3)

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

    boolean[] removed = new boolean[objectList.size()];
    Arrays.fill(removed, false); // unnecessary
    List<Pair> 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;
                    }
                }
            }
        }
    }

答案 2 :(得分:1)

为什么不接受非功能性方法?

List<Pair<Object, Object>> 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)));
        }
    }
}

答案 3 :(得分:0)

如果可以订购列表,则可以采用另一种方法:

  • 对列表进行排序

循环:

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

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

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

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