2016-07-08 4 views
5

Gibt es einen besseren, einfacheren Ansatz für dieses Problem?Kreuzung von Strom von Mengen in neue Menge

@Test 
public void testReduce() { 
    Set<Integer> foo = ImmutableSet.of(1,2,3,4,8,9); 
    Set<Integer> bar = ImmutableSet.of(1,3,8,5,11); 

    //DO think about solution for 1..n sets, and not only two. 
    Set<Integer> intersection = ImmutableList.of(foo,bar) 
      .stream() 
      .reduce(null, (a, b) -> { 
       if (a == null) { 
        a = new HashSet<Integer>(b); 
       } 
       else { 
        a.retainAll(b); 
       } 
       return a; 
      }); 
    assertThat(intersection, is(ImmutableSet.of(1,3,8))); 
} 
+1

http://stackoverflow.com/questions/31683375/java-8-lambda-intersection-of-two-lists – Wilson

+1

Statt 'ImmutableList.of (foo, bar) .stream()' Sie können einfach verwenden ' Stream.of (foo, bar) '... – Holger

+0

@Wilson: Ich habe diese Frage gesehen. Ich brauche eine Stream-Kreuzung, nicht nur zwei Sätze –

Antwort

5

reduce ist die falsche Methode dafür, da Sie das Argument der Funktion nicht ändern dürfen diesen Weg. Dies ist eine wandelbar Reduktion, die auch als collect bekannt:

List<Set<Integer>> listOfSets=…; 
//check if at least one set is in the list 
Set<Integer> intersection = listOfSets.stream().skip(1) 
    .collect(()->new HashSet<>(listOfSets.get(0)), Set::retainAll, Set::retainAll); 

Nachdem für den ersten Satz spähen ist ein Unentschieden wieder hier, aber mit null als Identitätswert nicht sauber ist entweder (und würde nicht funktionieren mit collect, da der Akku keinen neuen Satz zurückgeben kann).

+0

Wow. Nette Lösung. Eine Frage: Wir könnten das erste Element aus dem Stream überspringen. Ist es möglich? –

+2

Also listOfSets.stream(). Skip (1) sollte den Trick tun –

0

Sie können die Elemente von foo durch Überprüfung auswählen, wenn sie in den bar oder otherBar Sets sind:

Set<Integer> set = 
    foo.stream().filter(e -> Stream.of(bar, otherBar).allMatch(s -> s.contains(e))).collect(toSet()); 

Zum Beispiel, wenn Sie erhalten ein Collection<Set<T>> wie Sie sagten:

public static <T> Set<T> intersection(Collection<Set<T>> input) { 
    if(input.isEmpty()) { 
     return Collections.emptySet(); 
    } else { 
     Set<T> first = input.iterator().next(); 
     //if the collection allows to remove element, you can remove first before 
     return first.stream().filter(e -> input.stream().allMatch(s -> s.contains(e))).collect(toSet()); 
    } 
} 
+0

In dem Beispiel habe ich 2 Sätze. Die Lösung sollte für eine beliebige Anzahl von Elementen funktionieren und sollte nicht jedes Mal ein neues Set erstellen, wenn der Reducer ausgeführt wird. –

1

Diese Antwort gilt nur für Ganzzahlsätze, keine generischen Sätze. Aber für jemanden, der nach Geschwindigkeit sucht, sind Listen von Ganzzahlen manchmal ein guter Fall für komprimierte Bitmaps. Sie sollten, wenn Sie Ihre ganze Zahlen überprüfen Gruppe gut und in einigen Fällen können Sie einige Größenordnungen auf Geschwindigkeit gewinnen, wenn Sie diese (mit com.googlecode.javaewah32, Apache 2.0 Lizenz) tun:

Set<Integer> foo = ImmutableSet.of(1,2,3,4,8,9); 
    Set<Integer> bar = ImmutableSet.of(1,3,8,5,11); 

    EWAHCompressedBitmap32 fooBitmap = new EWAHCompressedBitmap32(); 
    EWAHCompressedBitmap32 barBitmap = new EWAHCompressedBitmap32(); 

    //fill bitmaps 
    foo.stream().forEach(fooBitmap::set); 
    bar.stream().forEach(barBitmap::set); 

    //fooBitmap.and(barBitmap) returns intersection of sets now. fast! 
    ImmutableSet<Integer> intersection = ImmutableSet.<Integer>builder() 
            .addAll(fooBitmap.and(barBitmap)) 
            .build(); 

    System.out.println(intersection); 

Dieser Code ist nur ein Beispiel. Sie könnten/sollten einen anderen Ansatz verwenden, um in die resultierende Menge zu konvertieren. EWAHCompressedBitmap32 ist Iterable<Integer> also, der Phantasie sind keine Grenzen gesetzt.

Jetzt schneidet der Code oben nur 2 Sätze. Um alle Sätze in der Liste überschneiden, können Sie die üblichen tun reduzieren:

Set<Integer> foo = ImmutableSet.of(1,2,3,4,8,9); 
    Set<Integer> bar = ImmutableSet.of(1,3,8,5,11); 

    List<Set<Integer>> sets = ImmutableList.of(foo,bar); 

    EWAHCompressedBitmap32 res = sets.stream().map(l -> { 
     EWAHCompressedBitmap32 b = new EWAHCompressedBitmap32(); 
     l.stream().forEach(b::set); 
     return b; 
    }).reduce(null, (l, r) -> l == null ? r : l.and(r)); 

    System.out.println(res); 

Noch eine weitere Alternative Reduktionssammler zu verwenden ist:

EWAHCompressedBitmap32 res = sets.stream().collect(Collectors.reducing(
     //identity 
     null, 
     //mapper set -> compressedBitmap 
     l -> { 
      EWAHCompressedBitmap32 b = new EWAHCompressedBitmap32(); 
      l.stream().forEach(b::set); 
      return b; 
     }, 
     //and-reducer 
     (l, r) -> l == null ? r : l.and(r) 
)); 
3

Im Folgenden wird funktionieren, wenn Sie verwenden Eclipse Collections:

@Test 
public void testReduce() 
{ 
    ImmutableSet<Integer> foo = Sets.immutable.of(1, 2, 3, 4, 8, 9); 
    ImmutableSet<Integer> bar = Sets.immutable.of(1, 3, 8, 5, 11); 

    // Works with Eclipse Collections 7.0 or above 
    ImmutableSet<Integer> intersection1 = Lists.mutable.of(foo, bar) 
      .stream() 
      .reduce(ImmutableSet::intersect).get(); 
    Assert.assertEquals(intersection1, Sets.immutable.of(1, 3, 8)); 

    // Works with Eclipse Collections 8.0.0-M1 or above 
    ImmutableSet<Integer> intersection2 = Lists.immutable.of(foo, bar) 
      .reduce(ImmutableSet::intersect).get(); 
    Assert.assertEquals(intersection2, Sets.immutable.of(1, 3, 8)); 
} 

Dies kann auch mit MutableSet arbeiten.

@Test 
public void testReduce() 
{ 
    MutableSet<Integer> foo = Sets.mutable.of(1, 2, 3, 4, 8, 9); 
    MutableSet<Integer> bar = Sets.mutable.of(1, 3, 8, 5, 11); 

    // Works with Eclipse Collections 7.0 or above 
    MutableSet<Integer> intersection1 = Lists.mutable.of(foo, bar) 
      .stream() 
      .reduce(MutableSet::intersect).get(); 
    Assert.assertEquals(intersection1, Sets.immutable.of(1, 3, 8)); 

    // Works with Eclipse Collections 8.0.0-M1 or above 
    MutableSet<Integer> intersection2 = Lists.immutable.of(foo, bar) 
      .reduce(MutableSet::intersect).get(); 
    Assert.assertEquals(intersection2, Sets.immutable.of(1, 3, 8)); 
} 

in Eclipse Collections, ImmutableSet erstreckt sich nicht java.util.Set, wie Set eine veränderbare Schnittstelle ist. MutableSet erweitert sich java.util.Set. Diese Designauswahl wird in der Antwort auf diese question erläutert.

Hinweis: Ich bin ein Committer für Eclipse Collections.

+0

Schöne Beispiele. Ich werde mir Eclipse Collections anschauen. Es scheint interessant zu sein. –

Verwandte Themen