简体   繁体   中英

Which Collectors to use?

Regarding those classes I'm trying to build a Map :

With these simple classes structures

class Alert {

     public String serie;
     public String rame;
     public String label;

     public Alert( String serie, String rame, String label ) {
         this.serie = serie;
         this.rame = rame;
         this.label = label;
     }

     public String getSerie() {
         return serie;
     }

     public String getRame() {
         return rame;
     }

     public String getLabel() {
         return label;
     }
}

 class Diagnostic {
    public String serie;
    public String rame;

    public List< Event > events;

    public Diagnostic(String serie, String rame){
         this.serie = serie;
         this.rame = rame;
    }

    public void addEvent(String label){
         if(events == null)events = new ArrayList<>(  );
         events.add( new Event( label ) );
    }
 }

class Event {

    public String label;

    public Event(String label){
         this.label = label;
     )}
 }


 Alert atgv1 = new Alert( "tgv", "rame1", "label1" );
 Alert atgv2 = new Alert( "tgv", "rame1", "label2" );
 Alert atgv3 = new Alert( "tgv", "rame1" , "label3");

 Alert atgv4 = new Alert( "tgv", "rame2", "label4" );
 Alert atgv5 = new Alert( "tgv", "rame2", "label5" );

 Alert atgv6 = new Alert( "tgv", "rame3", "label6" );

 Alert az2n1 = new Alert( "z2n", "rame1", "label1" );
 Alert az2n2 = new Alert( "z2n", "rame1", "label2" );
 Alert az2n3 = new Alert( "z2n", "rame1" , "label3");

 Alert az2n4 = new Alert( "z2n", "rame2", "label4" );
 Alert az2n5 = new Alert( "z2n", "rame2", "label5" );
 Alert az2n6 = new Alert( "z2n", "rame3", "label6" );

 List<Alert> alerts = Arrays.asList(atgv1, atgv2, atgv3, atgv4, atgv5, atgv6, az2n1, az2n2, az2n3, az2n4, az2n5, az2n6);

I have this code that works :

Map<String, Map<String, Diagnostic>> map = alerts.stream()
            .collect(Collectors.collectingAndThen( Collectors.groupingBy( Alert::getSerie,
                    Collectors.toMap( Alert::getRame,  a -> new Diagnostic( a.getSerie(), a.getRame() ), (a1, a2) -> a1)), b -> {

                b.forEach( ( k, v ) -> v.forEach( ( y, z)  -> {
                    List<Alert> found = alerts.stream().filter( p -> p.getSerie().equals( k ) && p.getRame().equals( y ) ).collect( Collectors.toList());

                    found.forEach( f -> z.addEvent( f.getLabel() ) );
                }) );
                return b;
            } ));

The result is what I want however the problem is that I'm looping on the maps in the andThen in order to find the right alert (filter) and add the event (.addEvent)

How to achieve it in one time? Is it possible without coding my own Collector ? The difficult part is that I want a single Diagnostic with its List of Event and NOT a List of Diagnostic that i can easily achieve with a second groupingBy instead of the toMap ...

You can approach the problem with multiple groupingBy , what you would then be looking for is reducing the Diagnostic values:

Map<String, Map<String, Optional<Diagnostic>>> map = alerts.stream()
    .collect(Collectors.groupingBy(Alert::getSerie,
        Collectors.groupingBy(Alert::getRame,
            Collectors.mapping(e -> new Diagnostic(e.getSerie(), e.getRame()),
                Collectors.reducing((d1, d2) -> Diagnostic.builder()
                    .serie(d1.getSerie())
                    .rame(d1.getRame())
                    .events(Stream.concat(d1.getEvents().stream(),
                        d2.getEvents().stream()).collect(Collectors.toList()))
                    .build())))));

Note : The above relies on the Builder that I've used for the convenience of creating the instance of Diagnostic involving the addition of events and it collects the innermost values as Optional for the lack of identity element to fall back upon.

Or as annswered by Holger, using toMap(f1, f2, f3) instead of groupingBy(f1, mapping(f2, reducing(f3))) would be a preferred operation for the nested collecting operation, as in:

Map<String, Map<String, Diagnostic>> map = alerts.stream()
    .collect(Collectors.groupingBy(Alert::getSerie,
        Collectors.toMap(Alert::getRame, e -> new Diagnostic(e.getSerie(), e.getRame()),
            (d1, d2) -> Diagnostic.builder()
                .serie(d1.getSerie())
                .rame(d1.getRame())
                .events(Stream.concat(d1.getEvents().stream(),
                    d2.getEvents().stream()).collect(Collectors.toList()))
                .build())));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM