Jak sprawdzić, czy dwa strumienie są rozłączne?

Nov 23 2020

Chciałbym porównać ze strumieniami i sprawdzić, czy mają one 1 lub więcej wspólnych elementów (znalezienie 1 wystarczy, aby przestać szukać więcej). Chcę móc zastosować to do strumieni zawierających niestandardową klasę.

Dla ilustracji załóżmy, że mam klasę, która wygląda następująco:

public class Point {
    public final int row;
    public final int col;

    public Point(int row, int col) {
        this.row = row;
        this.col = col;
    }    

    @Override
    public boolean equals(Object obj) {
        if (obj == null) return false;
        if (obj.getClass() != this.getClass()) return false;
        final Point other = (Point) obj;
        return this.row == other.row && this.col == other.col;
    }

    @Override
    public int hashCode() {
        return Objects.hash(row, col); 
    }
}

A potem mam dwa urocze strumienie, które wyglądają następująco:

Stream<Point> streamA = Stream.of(new Point(2, 5), new Point(3, 1));
Stream<Point> streamB = Stream.of(new Point(7, 3), new Point(3, 1));

Biorąc pod uwagę, że te strumienie mają 1 punkt wspólny (mianowicie Point(3, 1)), chciałbym, aby ostateczny wynik był prawdziwy.

Pożądaną funkcjonalność można przedstawić jako:

public static boolean haveSomethingInCommon(Stream<Point> a, Stream<Point> b){
    //Code that compares a and b and returns true if they have at least 1 element in common
}

Odpowiedzi

1 YCF_L Nov 23 2020 at 14:37

Przede wszystkim musisz przekonwertować swoje strumienie na zestaw lub listę, aby nie otrzymać słynnego błędu:

java.lang.IllegalStateException: stream has already been operated upon or closed

A następnie możesz użyć anyMatchtego:

public static boolean haveSomethingInCommon(Stream<Coord> a, Stream<Coord> b) {
    Set<Coord> setA = a.collect(Collectors.toSet());
    Set<Coord> setB = b.collect(Collectors.toSet());

    return setA.stream().anyMatch(setB::contains);
}

Lub możesz przekonwertować tylko bstrumień na zestaw i użyć:

public static boolean haveSomethingInCommon(Stream<Coord> a, Stream<Coord> b) {
    Set<Coord> setB = b.collect(Collectors.toSet());
    return a.anyMatch(setB::contains);
}

Polecam Set<Coord>zamiast Stream<Coord>jako param w swojej metodzie.

public static boolean haveSomethingInCommon(Set<Coord> a, Set<Coord> b) {
    return a.stream().anyMatch(b::contains);
}
2 Naman Nov 23 2020 at 16:17

Bez niezależnego zbierania dwóch strumieni można grupować i identyfikować, czy do dowolnego klucza jest mapowanych wiele wartości.

public static boolean haveSomethingInCommon(Stream<Coord> a, Stream<Coord> b) {
    return Stream.concat(a, b)
            .collect(Collectors.groupingBy(Function.identity()))
            .values().stream()
            .anyMatch(l -> l.size() > 1);
}

Jeśli ten sam strumień może mieć ten sam element dwa razy lub więcej , możesz zmienić używany kod -

Stream.concat(a.distinct(), b.distinct())
Kaplan Nov 23 2020 at 18:19

jest funkcja disjoint w Collections :

public static boolean haveSomethingInCommon( Stream<Coord> a, Stream<Coord> b ) {
  return( ! Collections.disjoint( a.collect( toList() ), b.collect( toList() ) ) );
}