Znalazłem kod do grupowania obiektów według jakiejś nazwy pola z POJO. Poniżej znajduje się kod:
public class Temp {
static class Person {
private String name;
private int age;
private long salary;
Person(String name, int age, long salary) {
this.name = name;
this.age = age;
this.salary = salary;
}
@Override
public String toString() {
return String.format("Person{name='%s', age=%d, salary=%d}", name, age, salary);
}
}
public static void main(String[] args) {
Stream<Person> people = Stream.of(new Person("Paul", 24, 20000),
new Person("Mark", 30, 30000),
new Person("Will", 28, 28000),
new Person("William", 28, 28000));
Map<Integer, List<Person>> peopleByAge;
peopleByAge = people
.collect(Collectors.groupingBy(p -> p.age, Collectors.mapping((Person p) -> p, toList())));
System.out.println(peopleByAge);
}
}
Wynik to (co jest poprawne):
{24=[Person{name='Paul', age=24, salary=20000}], 28=[Person{name='Will', age=28, salary=28000}, Person{name='William', age=28, salary=28000}], 30=[Person{name='Mark', age=30, salary=30000}]}
Ale co, jeśli chcę grupować według wielu pól? Mogę oczywiście przekazać niektóre POJO w groupingBy()
metodzie po zaimplementowaniu equals()
metody w tym POJO, ale czy jest jakaś inna opcja, jak mogę grupować według więcej niż jednego pola z danego POJO?
Np. Tutaj w moim przypadku chcę grupować według imienia i wieku.
mapping
jako dalszy kolektor jest zbędny w przesłanym kodzie.people.collect(groupingBy(p -> Arrays.asList(p.name, p.age)))
.Odpowiedzi:
Masz tutaj kilka opcji. Najprościej jest połączyć kolekcjonerów w łańcuch:
Następnie, aby uzyskać listę 18-letnich osób o imieniu Fred, użyłbyś:
map.get("Fred").get(18);
Drugą opcją jest zdefiniowanie klasy reprezentującej grupowanie. Może to być wewnątrz osoby. Ten kod używa a,
record
ale równie dobrze mógłby być klasą (zequals
ihashCode
zdefiniowaną) w wersjach Java przed dodaniem JEP 359:class Person { record NameAge(String name, int age) { } public NameAge getNameAge() { return new NameAge(name, age); } }
Następnie możesz użyć:
i szukaj za pomocą
map.get(new NameAge("Fred", 18));
Wreszcie, jeśli nie chcesz implementować własnego rekordu grupowego, to wiele struktur Javy ma
pair
klasę zaprojektowaną do tego typu rzeczy. Na przykład: para wspólna apache Jeśli używasz jednej z tych bibliotek, możesz uczynić klucz do mapy parę zawierającą imię i wiek:i pobierz za pomocą:
map.get(Pair.of("Fred", 18));
Osobiście nie widzę zbytniej wartości w krotkach ogólnych, teraz, gdy rekordy są dostępne w języku, ponieważ rekordy lepiej prezentują intencje i wymagają bardzo mało kodu.
źródło
Function<T,U>
również ukrywa zamiary w tym sensie - ale nie zobaczysz nikogo deklarującego swój własny funkcjonalny interfejs dla każdego kroku mapowania; intencja jest już obecna w treści lambda. To samo z krotkami: są świetne jako typy klejów między komponentami API. Klasy przypadków BTW Scala są IMHO wielką wygraną zarówno pod względem zwięzłości, jak i zamierzonego ujawnienia.NameAge
jako jedną wkładką:case class NameAge { val name: String; val age: Int }
--- i maszequals
,hashCode
itoString
!Map<String, Map<Integer, List<Person>>> map
Spójrz na kod:
Możesz po prostu stworzyć funkcję i pozwolić jej wykonać pracę za Ciebie, rodzaj funkcjonalnego stylu!
Teraz możesz użyć go jako mapy:
Twoje zdrowie!
źródło
groupingBy
Metoda pierwszy parametr jestFunction<T,K>
, gdy:Jeśli zamienimy lambdę na klasę anonimową w Twoim kodzie, zobaczymy coś takiego:
people.stream().collect(Collectors.groupingBy(new Function<Person, int>() { @Override public int apply(Person person) { return person.getAge(); } }));
Teraz zmień parametr wyjściowy
<K>
. W tym przypadku, na przykład, użyłem klasy pary z org.apache.commons.lang3.tuple do grupowania według nazwy i wieku, ale możesz utworzyć własną klasę do filtrowania grup według potrzeb.people.stream().collect(Collectors.groupingBy(new Function<Person, Pair<Integer, String>>() { @Override public YourFilter apply(Person person) { return Pair.of(person.getAge(), person.getName()); } }));
Wreszcie po zamianie na lambda z powrotem kod wygląda tak:
źródło
List<String>
?Cześć, możesz po prostu połączyć swoje,
groupingByKey
takie jakMap<String, List<Person>> peopleBySomeKey = people .collect(Collectors.groupingBy(p -> getGroupingByKey(p), Collectors.mapping((Person p) -> p, toList()))); //write getGroupingByKey() function private String getGroupingByKey(Person p){ return p.getAge()+"-"+p.getName(); }
źródło
Zdefiniuj klasę dla definicji klucza w swojej grupie.
class KeyObj { ArrayList<Object> keys; public KeyObj( Object... objs ) { keys = new ArrayList<Object>(); for (int i = 0; i < objs.length; i++) { keys.add( objs[i] ); } } // Add appropriate isEqual() ... you IDE should generate this }
Teraz w twoim kodzie
peopleByManyParams = people .collect(Collectors.groupingBy(p -> new KeyObj( p.age, p.other1, p.other2 ), Collectors.mapping((Person p) -> p, toList())));
źródło
Ararys.asList()
- co przy okazji jest dobrym wyborem w przypadku OP.Pair
przykładzie wymienionym w innym przykładzie, ale bez ograniczenia argumentów.hashCode
) raz)Możesz użyć listy jako klasyfikatora dla wielu pól, ale musisz zawinąć wartości null w opcjonalne:
źródło
Musiałem zrobić raport dla firmy cateringowej, która serwuje obiady dla różnych klientów. Innymi słowy, catering może mieć na lub więcej firm, które przyjmują zamówienia z cateringu i musi wiedzieć, ile obiadów musi produkować każdego dnia dla wszystkich swoich klientów!
Aby zauważyć, nie użyłem sortowania, aby nie komplikować zbytnio tego przykładu.
To jest mój kod:
@Test public void test_2() throws Exception { Firm catering = DS.firm().get(1); LocalDateTime ldtFrom = LocalDateTime.of(2017, Month.JANUARY, 1, 0, 0); LocalDateTime ldtTo = LocalDateTime.of(2017, Month.MAY, 2, 0, 0); Date dFrom = Date.from(ldtFrom.atZone(ZoneId.systemDefault()).toInstant()); Date dTo = Date.from(ldtTo.atZone(ZoneId.systemDefault()).toInstant()); List<PersonOrders> LON = DS.firm().getAllOrders(catering, dFrom, dTo, false); Map<Object, Long> M = LON.stream().collect( Collectors.groupingBy(p -> Arrays.asList(p.getDatum(), p.getPerson().getIdfirm(), p.getIdProduct()), Collectors.counting())); for (Map.Entry<Object, Long> e : M.entrySet()) { Object key = e.getKey(); Long value = e.getValue(); System.err.println(String.format("Client firm :%s, total: %d", key, value)); } }
źródło
Tak zrobiłem grupowanie według wielu pól branchCode i prdId, po prostu wysyłając to dla kogoś w potrzebie
import java.math.BigDecimal; import java.math.BigInteger; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.stream.Collectors; /** * * @author charudatta.joshi */ public class Product1 { public BigInteger branchCode; public BigInteger prdId; public String accountCode; public BigDecimal actualBalance; public BigDecimal sumActBal; public BigInteger countOfAccts; public Product1() { } public Product1(BigInteger branchCode, BigInteger prdId, String accountCode, BigDecimal actualBalance) { this.branchCode = branchCode; this.prdId = prdId; this.accountCode = accountCode; this.actualBalance = actualBalance; } public BigInteger getCountOfAccts() { return countOfAccts; } public void setCountOfAccts(BigInteger countOfAccts) { this.countOfAccts = countOfAccts; } public BigDecimal getSumActBal() { return sumActBal; } public void setSumActBal(BigDecimal sumActBal) { this.sumActBal = sumActBal; } public BigInteger getBranchCode() { return branchCode; } public void setBranchCode(BigInteger branchCode) { this.branchCode = branchCode; } public BigInteger getPrdId() { return prdId; } public void setPrdId(BigInteger prdId) { this.prdId = prdId; } public String getAccountCode() { return accountCode; } public void setAccountCode(String accountCode) { this.accountCode = accountCode; } public BigDecimal getActualBalance() { return actualBalance; } public void setActualBalance(BigDecimal actualBalance) { this.actualBalance = actualBalance; } @Override public String toString() { return "Product{" + "branchCode:" + branchCode + ", prdId:" + prdId + ", accountCode:" + accountCode + ", actualBalance:" + actualBalance + ", sumActBal:" + sumActBal + ", countOfAccts:" + countOfAccts + '}'; } public static void main(String[] args) { List<Product1> al = new ArrayList<Product1>(); System.out.println(al); al.add(new Product1(new BigInteger("01"), new BigInteger("11"), "001", new BigDecimal("10"))); al.add(new Product1(new BigInteger("01"), new BigInteger("11"), "002", new BigDecimal("10"))); al.add(new Product1(new BigInteger("01"), new BigInteger("12"), "003", new BigDecimal("10"))); al.add(new Product1(new BigInteger("01"), new BigInteger("12"), "004", new BigDecimal("10"))); al.add(new Product1(new BigInteger("01"), new BigInteger("12"), "005", new BigDecimal("10"))); al.add(new Product1(new BigInteger("01"), new BigInteger("13"), "006", new BigDecimal("10"))); al.add(new Product1(new BigInteger("02"), new BigInteger("11"), "007", new BigDecimal("10"))); al.add(new Product1(new BigInteger("02"), new BigInteger("11"), "008", new BigDecimal("10"))); al.add(new Product1(new BigInteger("02"), new BigInteger("12"), "009", new BigDecimal("10"))); al.add(new Product1(new BigInteger("02"), new BigInteger("12"), "010", new BigDecimal("10"))); al.add(new Product1(new BigInteger("02"), new BigInteger("12"), "011", new BigDecimal("10"))); al.add(new Product1(new BigInteger("02"), new BigInteger("13"), "012", new BigDecimal("10"))); //Map<BigInteger, Long> counting = al.stream().collect(Collectors.groupingBy(Product1::getBranchCode, Collectors.counting())); // System.out.println(counting); //group by branch code Map<BigInteger, List<Product1>> groupByBrCd = al.stream().collect(Collectors.groupingBy(Product1::getBranchCode, Collectors.toList())); System.out.println("\n\n\n" + groupByBrCd); Map<BigInteger, List<Product1>> groupByPrId = null; // Create a final List to show for output containing one element of each group List<Product> finalOutputList = new LinkedList<Product>(); Product newPrd = null; // Iterate over resultant Map Of List Iterator<BigInteger> brItr = groupByBrCd.keySet().iterator(); Iterator<BigInteger> prdidItr = null; BigInteger brCode = null; BigInteger prdId = null; Map<BigInteger, List<Product>> tempMap = null; List<Product1> accListPerBr = null; List<Product1> accListPerBrPerPrd = null; Product1 tempPrd = null; Double sum = null; while (brItr.hasNext()) { brCode = brItr.next(); //get list per branch accListPerBr = groupByBrCd.get(brCode); // group by br wise product wise groupByPrId=accListPerBr.stream().collect(Collectors.groupingBy(Product1::getPrdId, Collectors.toList())); System.out.println("===================="); System.out.println(groupByPrId); prdidItr = groupByPrId.keySet().iterator(); while(prdidItr.hasNext()){ prdId=prdidItr.next(); // get list per brcode+product code accListPerBrPerPrd=groupByPrId.get(prdId); newPrd = new Product(); // Extract zeroth element to put in Output List to represent this group tempPrd = accListPerBrPerPrd.get(0); newPrd.setBranchCode(tempPrd.getBranchCode()); newPrd.setPrdId(tempPrd.getPrdId()); //Set accCOunt by using size of list of our group newPrd.setCountOfAccts(BigInteger.valueOf(accListPerBrPerPrd.size())); //Sum actual balance of our of list of our group sum = accListPerBrPerPrd.stream().filter(o -> o.getActualBalance() != null).mapToDouble(o -> o.getActualBalance().doubleValue()).sum(); newPrd.setSumActBal(BigDecimal.valueOf(sum)); // Add product element in final output list finalOutputList.add(newPrd); } } System.out.println("+++++++++++++++++++++++"); System.out.println(finalOutputList); } }
Wyjście jest jak poniżej:
+++++++++++++++++++++++ [Product{branchCode:1, prdId:11, accountCode:null, actualBalance:null, sumActBal:20.0, countOfAccts:2}, Product{branchCode:1, prdId:12, accountCode:null, actualBalance:null, sumActBal:30.0, countOfAccts:3}, Product{branchCode:1, prdId:13, accountCode:null, actualBalance:null, sumActBal:10.0, countOfAccts:1}, Product{branchCode:2, prdId:11, accountCode:null, actualBalance:null, sumActBal:20.0, countOfAccts:2}, Product{branchCode:2, prdId:12, accountCode:null, actualBalance:null, sumActBal:30.0, countOfAccts:3}, Product{branchCode:2, prdId:13, accountCode:null, actualBalance:null, sumActBal:10.0, countOfAccts:1}]
Po sformatowaniu:
[ Product{branchCode:1, prdId:11, accountCode:null, actualBalance:null, sumActBal:20.0, countOfAccts:2}, Product{branchCode:1, prdId:12, accountCode:null, actualBalance:null, sumActBal:30.0, countOfAccts:3}, Product{branchCode:1, prdId:13, accountCode:null, actualBalance:null, sumActBal:10.0, countOfAccts:1}, Product{branchCode:2, prdId:11, accountCode:null, actualBalance:null, sumActBal:20.0, countOfAccts:2}, Product{branchCode:2, prdId:12, accountCode:null, actualBalance:null, sumActBal:30.0, countOfAccts:3}, Product{branchCode:2, prdId:13, accountCode:null, actualBalance:null, sumActBal:10.0, countOfAccts:1} ]
źródło