Encontrei o código para agrupar os objetos por algum nome de campo do POJO. Abaixo está o código para isso:
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);
}
}
E a saída é (que está correta):
{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}]}
Mas e se eu quiser agrupar por vários campos? Obviamente, posso passar algum POJO no groupingBy()
método após implementar o equals()
método nesse POJO, mas existe alguma outra opção como se eu pudesse agrupar por mais de um campo do POJO fornecido?
Por exemplo, aqui no meu caso, quero agrupar por nome e idade.
mapping
como um coletor downstream é redundante no código que você postou.people.collect(groupingBy(p -> Arrays.asList(p.name, p.age)))
.Respostas:
Você tem algumas opções aqui. O mais simples é acorrentar seus coletores:
Então, para obter uma lista de pessoas de 18 anos chamadas Fred, você usaria:
map.get("Fred").get(18);
Uma segunda opção é definir uma classe que represente o agrupamento. Isso pode estar dentro de Pessoa. Este código usa um,
record
mas poderia facilmente ser uma classe (comequals
ehashCode
definida) nas versões do Java antes da adição do JEP 359:class Person { record NameAge(String name, int age) { } public NameAge getNameAge() { return new NameAge(name, age); } }
Então você pode usar:
e pesquisar com
map.get(new NameAge("Fred", 18));
Finalmente, se você não deseja implementar seu próprio registro de grupo, muitos dos frameworks Java existentes têm uma
pair
classe projetada para esse tipo de coisa. Por exemplo: apache commons pair Se você usar uma dessas bibliotecas, poderá transformar a chave do mapa em um par de nome e idade:e recupere com:
map.get(Pair.of("Fred", 18));
Pessoalmente, não vejo muito valor em tuplas genéricas, agora que os registros estão disponíveis na linguagem, pois os registros exibem melhor a intenção e exigem muito pouco código.
fonte
Function<T,U>
também esconde a intenção neste sentido --- mas você não verá ninguém declarando sua própria interface funcional para cada etapa de mapeamento; a intenção já está lá no corpo lambda. O mesmo acontece com as tuplas: elas são ótimas como tipos de cola entre os componentes da API. As classes de caso do BTW Scala são IMHO uma grande vitória em termos de concisão e exposição intencional.NameAge
como um one-liner:case class NameAge { val name: String; val age: Int }
--- e você começaequals
,hashCode
etoString
!Map<String, Map<Integer, List<Person>>> map
Aqui, olhe o código:
Você pode simplesmente criar uma Função e deixá-la fazer o trabalho por você, uma espécie de Estilo funcional!
Agora você pode usá-lo como um mapa:
Felicidades!
fonte
O
groupingBy
método tem o primeiro parâmetroFunction<T,K>
onde:Se substituirmos lambda pela classe anônima em seu código, poderemos ver algum tipo disso:
people.stream().collect(Collectors.groupingBy(new Function<Person, int>() { @Override public int apply(Person person) { return person.getAge(); } }));
Agora altere o parâmetro de saída
<K>
. Neste caso, por exemplo, usei uma classe de pares de org.apache.commons.lang3.tuple para agrupar por nome e idade, mas você pode criar sua própria classe para filtrar grupos conforme necessário.people.stream().collect(Collectors.groupingBy(new Function<Person, Pair<Integer, String>>() { @Override public YourFilter apply(Person person) { return Pair.of(person.getAge(), person.getName()); } }));
Finalmente, depois de substituir por lambda de volta, o código fica assim:
fonte
List<String>
?Olá, você pode simplesmente concatenar seu
groupingByKey
, comoMap<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(); }
fonte
Defina uma classe para definição de chave em seu grupo.
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 }
Agora em seu código,
peopleByManyParams = people .collect(Collectors.groupingBy(p -> new KeyObj( p.age, p.other1, p.other2 ), Collectors.mapping((Person p) -> p, toList())));
fonte
Ararys.asList()
--- o que é uma boa escolha para o caso da OP.Pair
exemplo mencionado no outro exemplo, mas sem limite de argumento.hashCode
) uma vez)Você pode usar List como um classificador para muitos campos, mas precisa agrupar valores nulos em Optional:
fonte
Precisava fazer um relatório para uma empresa de catering que serve almoços para vários clientes. Ou seja, a restauração pode ter uma ou mais empresas que recebem encomendas da restauração e deve saber quantos almoços deve produzir todos os dias para todos os seus clientes!
Só para notar, não usei ordenação, para não complicar muito este exemplo.
Este é o meu código:
@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)); } }
fonte
Foi assim que fiz o agrupamento por vários campos branchCode e prdId, apenas postando para quem precisa
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); } }
O resultado é o seguinte:
+++++++++++++++++++++++ [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}]
Depois de formatá-lo:
[ 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} ]
fonte