Java: Forma correcta de crear una lista que contiene todos los no-en-se cruzan elementos de dos listas diferentes en función de un atributo específico?

Rüdiger:

Dadas dos listas de objetos, me gustaría ser capaz de decir qué elementos no están en su intersecan basado en uno de sus atributos. Echemos un vistazo en el siguiente ejemplo:

Tengo una clase Fooque tiene dos atributos: booyplaceholder

class Foo {
    private int boo;
    private int placeholder = 1;

    public Foo(int boo) {
        this.boo = boo;
    }

    public int getBoo() {
        return boo;
    }
}

Ahora estoy creando dos listas desde que (digamos que esta es mi entrada)

    List<Foo> list1 = new ArrayList<Foo>();
    list1.add(new Foo(1));
    list1.add(new Foo(2));
    list1.add(new Foo(3));

    List<Foo> list2 = new ArrayList<Foo>();
    list2.add(new Foo(0));
    list2.add(new Foo(1));
    list2.add(new Foo(2));

Y ahora me gustaría decir, que los artículos están en list1y no en list2o en list2y no en list1base a su atributo boo. Así, en el ejemplo anterior quiero una List<Foo> notInIntersectListque contiene uno Foo(0)y uno Foo(3).

    List<Foo> notInIntersectList = new ArrayList<Foo>();
    list1.forEach(li1foo -> {
        boolean inBothLists = false;
        list2.forEach(li2foo -> {
            if (li1foo.getBoo() == li2foo.getBoo()) {
                inBothLists = true;
            }
        });
        if (!inBothLists) {
            notInIntersectList.add(li1foo);
        }
    });
    //now I covered all items in list1 but not in list2. Now do this again with lists swapped, so I can also cover those.
    //...

Por desgracia yo estoy haciendo Local variable inBothLists defined in an enclosing scope must be final or effectively finalcomo un error. ¿Cómo se resuelve este problema adecuadamente, ya que este no parece ser la solución "correcta"?

user7:

No se puede mutar las variables dentro de una expresión lambda (Ver: variable que se utiliza en la expresión lambda debe ser final o con eficacia definitiva )

He aquí una manera de arreglar su código (bien con las salas)

List<Foo> notInIntersectList = list1.stream()
        .filter(fooElementFromList1 -> list2
                .stream()
                .noneMatch(fooElementFromList2 -> fooElementFromList2.getBoo() == fooElementFromList1.getBoo()))
        .collect(Collectors.toCollection(ArrayList::new));

list2.stream()
        .filter(fooElementFromList2 -> list1
            .stream()
            .noneMatch(fooElementFromList1 -> fooElementFromList1.getBoo() == fooElementFromList2.getBoo()))
        .forEach(notInIntersectList::add);

La complejidad de este es O(n*m)(donde ny mson el número de elementos de list1 y list2 respectivamente).

Para hacer esto en O(n+m), puede utilizar un conjunto. Para ello, se necesita una equalsy hashcodemétodo en la Fooclase. Este considera dos Foocasos como igual sólo se basa en el valor de la variable de instancia boo.

class Foo {
    ....

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Foo other = (Foo) obj;
        return boo == other.boo;
    }

    @Override
    public int hashCode() {
        return boo;
    }
}

Y utilizar un Setpara esto

Set<Foo> fooSet1 = new HashSet<>(list1);
Set<Foo> fooSet2 = new HashSet<>(list2);

fooSet1.removeAll(list2);
fooSet2.removeAll(list1);

List<Foo> notInIntersectList = Stream.concat(fooSet1.stream(), fooSet2.stream())
            .collect(Collectors.toList());

Supongo que te gusta

Origin http://43.154.161.224:23101/article/api/json?id=177535&siteId=1
Recomendado
Clasificación