2016-04-22 9 views
2

Ich habe drei Klassen:Gruppe durch und summieren tief verschachtelte Listen mit Strömen

public class Supply { 
    private final List<Compartment> compartments; 

    public List<Compartment> getCompartments() { 
     return compartments; 
    } 

    public Supply(List<Compartment> compartments) { 
     this.compartments = compartments; 
    } 
} 

public class Compartment { 
    private final String itemId; 
    private final List<Unit> units; 

    public String getItemId() { 
     return itemId; 
    } 

    public List<Unit> getUnits() { 
     return units; 
    } 

    public Compartment(String itemId, List<Unit> units) { 
     this.itemId = itemId; 
     this.units = units; 
    } 
} 

public class Unit { 
    private final String containerNumber; 

    public String getContainerNumber() { 
     return containerNumber; 
    } 

    public Unit(String containerNumber) { 
     this.containerNumber = containerNumber; 
    } 
} 

diese Klassen gegeben, mag ich mit einer Liste, Karte, Tupel oder die Art der kommen: itemId und containerNumber Kombinationen und ihre Mengen.

Das heißt, wenn ich:

Unit unit1 = new Unit("unit"); 
Unit unit2 = new Unit("unit"); 
Unit unit3 = new Unit("another_unit"); 

Compartment compartment = new Compartment("foo", newArrayList(unit1, unit2, unit3)); 

Supply supply = new Supply(newArrayList(compartment)); 

Mit Java-Streams, würde Ich mag, wie etwas bekommen:

"foo", "unit", 2 
"foo", "another_unit", 1 

Ich habe eine Reihe von Kombinationen versucht, mit groupingBy, toMap, usw., ohne solches Glück.

Mein letzter Versuch ist so etwas wie:

Map<String, List<Map<String, List<Unit>>>> result = supply.getCompartments() 
    .stream() 
    .collect(groupingBy(Compartment::getItemId, 
      mapping(compartment -> compartment.getUnits().stream().collect(groupingBy(Unit::getContainerNumber)), toList()))); 

Was ich fühle mich ziemlich nahe ist, aber ich denke, ich keine Map da der Schlüssel verwenden könnte, itemId es könnte mehr Male mit einer anderen containerNumber

Antwort

1

Sie haben die richtige Idee. Was Ihnen fehlt, ist, dass Sie einen flachen Mapping-Collector anstelle eines Mapping-Collectors als Downstream-Collector benötigen.

Leider ist dieser Collector nicht in Java 8 integriert, aber er wird in Java 9 (JDK-8071600) mit Collectors.flatMapping(mapper, downstream) vorhanden sein. Für Java 8 kann man es wie folgt neu implementieren:

static <T,U,A,R> Collector<T,?,R> flatMapping(Function<? super T, ? extends Stream<? extends U>> mapper, Collector<? super U, A, R> downstream) { 
    BiConsumer<A, ? super U> downstreamAccumulator = downstream.accumulator(); 
    return Collector.of(
      downstream.supplier(), 
      (acc, t) -> { 
       try (Stream<? extends U> stream = mapper.apply(t)) { 
        if (stream != null) { 
         stream.sequential().forEach(u -> downstreamAccumulator.accept(acc, u)); 
        } 
       } 
      }, 
      downstream.combiner(), 
      downstream.finisher(), 
      downstream.characteristics().toArray(new Characteristics[downstream.characteristics().size()]) 
      ); 
} 

Dann können Sie diesen Sammler benutzen, um Ihre gewünschte Karte zu bauen:

Map<String, Map<String, List<Unit>>> map = 
    supply.getCompartments() 
      .stream() 
      .collect(groupingBy(
      Compartment::getItemId, 
      flatMapping(c -> c.getUnits().stream(), groupingBy(Unit::getContainerNumber)) 
     )); 
Verwandte Themen