2017-03-23 19 views
8

mam definicja POJO następująco:Java 8 Lambda Collectors.summingDługi kilka kolumn?

class EmployeeDetails{ 
private String deptName; 
private Double salary; 
private Double bonus; 
... 
} 

Obecnie mam wyrażenia lambda dla Grupy Przez 'deptName' jak:

$set.stream().collect(Collectors.groupingBy(EmployeeDetails::getDeptName, 
           Collectors.summingLong(EmployeeDetails::getSalary)); 

Pytanie Czy możliwe jest Sum więcej niż jednej kolumnie? Potrzebuję obliczyć sumę na zarówno pólw jednym wyrażeniu zamiast wielu razy?

SQL reprezentacja będzie:

SELECT deptName,SUM(salary),SUM(bonus) 
FROM TABLE_EMP 
GROUP BY deptName; 

Odpowiedz

6

Musisz utworzyć dodatkową klasę, która będzie posiadaczem 2 streszczone numerów (wynagrodzenie i bonus). I niestandardowy kolektor.

Powiedzmy masz

private static final class Summary { 
    private double salarySum; 
    private double bonusSum; 

    public Summary() { 
     this.salarySum = 0; 
     this.bonusSum = 0; 
    } 

    @Override 
    public String toString() { 
     return "Summary{" + 
       "salarySum=" + salarySum + 
       ", bonusSum=" + bonusSum + 
       '}'; 
    } 
} 

do przechowywania sum. Następnie trzeba kolektor tak:

private static class EmployeeDetailsSummaryCollector implements Collector<EmployeeDetails, Summary, Summary> { 
    @Override 
    public Supplier<Summary> supplier() { 
     return Summary::new; 
    } 

    @Override 
    public BiConsumer<Summary, EmployeeDetails> accumulator() { 
     return (summary, employeeDetails) -> { 
      summary.salarySum += employeeDetails.salary; 
      summary.bonusSum += employeeDetails.bonus; 
     }; 
    } 

    @Override 
    public BinaryOperator<Summary> combiner() { 
     return (summary, summary1) -> { 
      summary.salarySum += summary1.salarySum; 
      summary.bonusSum += summary1.bonusSum; 
      return summary; 
     }; 
    } 

    @Override 
    public Function<Summary, Summary> finisher() { 
     return Function.identity(); 
    } 

    @Override 
    public Set<Characteristics> characteristics() { 
     return EnumSet.of(Collector.Characteristics.IDENTITY_FINISH); 
    } 
} 

Z tych klas można zbierać swoje wyniki jak

final List<EmployeeDetails> employees = asList(
     new EmployeeDetails(/* deptName */"A", /* salary */ 100d, /* bonus */ 20d), 
     new EmployeeDetails("A", 150d, 10d), 
     new EmployeeDetails("B", 80d, 5d), 
     new EmployeeDetails("C", 100d, 20d) 
); 

final Collector<EmployeeDetails, Summary, Summary> collector = new EmployeeDetailsSummaryCollector(); 
final Map<String, Summary> map = employees.stream() 
     .collect(Collectors.groupingBy(o -> o.deptName, collector)); 
System.out.println("map = " + map); 

która drukuje to:

map = {A=[salary=250.0, bonus=30.0], B=[salary=80.0, bonus=5.0], C=[salary=100.0, bonus=20.0]} 
3

wiem, że masz swoją odpowiedź , ale tutaj jest moje zdanie (pisałem, a drugi został wysłany). W języku Java jest już Pair w postaci AbstractMap.SimpleEntry.

System.out.println(Stream.of(new EmployeeDetails("first", 50d, 7d), new EmployeeDetails("first", 50d, 7d), 
      new EmployeeDetails("second", 51d, 8d), new EmployeeDetails("second", 51d, 8d)) 
      .collect(Collectors.toMap(EmployeeDetails::getDeptName, 
        ed -> new AbstractMap.SimpleEntry<>(ed.getSalary(), ed.getBonus()), 
        (left, right) -> { 
         double key = left.getKey() + right.getKey(); 
         double value = left.getValue() + right.getValue(); 
         return new AbstractMap.SimpleEntry<>(key, value); 
        }, HashMap::new))); 
+0

To rozwiązanie działa również świetnie! ale ograniczone do dwóch kolumn, prawda? jeśli potrzebujemy 3 lub 4 kolumny, zwiększa się złożoność. – gpa

1

Grupowanie według jest operacją terminalową, która daje mapę. Mapa generowana przez groupingBy w poniższym kodzie to Map<String, List<EmployeeDetails>>. Tworzę nowy strumień przy użyciu metody Map entryet. Następnie tworzę nową Mapę używając Collectors.toMap. W tym podejściu zastosowano metodę łańcuchowania, aby uniknąć tworzenia innej klasy i tworzenia bardziej zwięzłego kodu.

details.stream() 
    .collect(Collectors.groupingBy(EmployeeDetails::getDeptName)) 
    .entrySet() 
    .stream() 
    .collect(Collectors.toMap(x->x.getKey(), x->x.getValue() 
     .stream() 
     .mapToDouble(y -> y.getSalary() + y.getBonus()) 
     .sum()));