Я нашел код для группировки объектов по имени некоторого поля из POJO. Ниже приведен код для этого:
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);
}
}
И вывод (что правильно):
{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}]}
Но что, если я хочу сгруппировать по нескольким полям? Я, очевидно, могу передать некоторый POJO в groupingBy()
методе после реализации equals()
метода в этом POJO, но есть ли какой-либо другой вариант, например, я могу группировать более чем по одному полям из данного POJO?
Например, в моем случае я хочу сгруппировать по имени и возрасту.
mapping
как нисходящий сборщик избыточен в опубликованном вами коде.people.collect(groupingBy(p -> Arrays.asList(p.name, p.age)))
.Ответы:
У вас есть несколько вариантов. Самый простой - связать ваших коллекционеров:
Затем, чтобы получить список 18-летних людей по имени Фред, вы должны использовать:
map.get("Fred").get(18);
Второй вариант - определить класс, представляющий группировку. Это может быть внутри Person. В этом коде используется,
record
но он также мог быть классом (с определеннымequals
иhashCode
определенным) в версиях Java до добавления JEP 359:class Person { record NameAge(String name, int age) { } public NameAge getNameAge() { return new NameAge(name, age); } }
Тогда вы можете использовать:
и искать с
map.get(new NameAge("Fred", 18));
Наконец, если вы не хотите реализовывать свою собственную групповую запись, тогда во многих фреймворках Java есть
pair
классы, предназначенные для этого типа вещей. Например: apache commons pair. Если вы используете одну из этих библиотек, вы можете сделать ключ к карте парой имени и возраста:и получить с помощью:
map.get(Pair.of("Fred", 18));
Лично я не вижу особой ценности в общих кортежах теперь, когда записи доступны на языке, поскольку записи лучше отображают намерение и требуют очень мало кода.
источник
Function<T,U>
также скрывает намерение в этом смысле - но вы не увидите, чтобы кто-нибудь объявлял свой собственный функциональный интерфейс для каждого шага сопоставления; намерение уже присутствует в теле лямбда. То же самое и с кортежами: они отлично подходят в качестве связующего типа между компонентами API. Кстати, классы кейсов Scala - это, IMHO, большая победа с точки зрения как краткости, так и намерения.NameAge
как однострочник:case class NameAge { val name: String; val age: Int }
--- и вы получитеequals
,hashCode
иtoString
!Map<String, Map<Integer, List<Person>>> map
Вот посмотрите на код:
Вы можете просто создать функцию и позволить ей делать всю работу за вас, своего рода функциональный стиль!
Теперь вы можете использовать его как карту:
Ура!
источник
groupingBy
Метод первый параметр ,Function<T,K>
где:Если мы заменим лямбда анонимным классом в вашем коде, мы увидим что-то вроде этого:
people.stream().collect(Collectors.groupingBy(new Function<Person, int>() { @Override public int apply(Person person) { return person.getAge(); } }));
Просто измените выходной параметр
<K>
. В этом случае, например, я использовал парный класс из org.apache.commons.lang3.tuple для группировки по имени и возрасту, но вы можете создать свой собственный класс для фильтрации групп по мере необходимости.people.stream().collect(Collectors.groupingBy(new Function<Person, Pair<Integer, String>>() { @Override public YourFilter apply(Person person) { return Pair.of(person.getAge(), person.getName()); } }));
Наконец, после замены лямбда обратно код выглядит так:
источник
List<String>
?Привет, вы можете просто объединить свои,
groupingByKey
напримерMap<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(); }
источник
Определите класс для определения ключа в вашей группе.
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 }
Теперь в вашем коде
peopleByManyParams = people .collect(Collectors.groupingBy(p -> new KeyObj( p.age, p.other1, p.other2 ), Collectors.mapping((Person p) -> p, toList())));
источник
Ararys.asList()
--- что, кстати, отличный выбор для случая OP.Pair
примеру, упомянутому в другом примере, но без ограничения аргументов.hashCode
) один раз)Вы можете использовать List в качестве классификатора для многих полей, но вам нужно обернуть нулевые значения в Optional:
источник
Мне нужно было составить отчет для кейтерингового предприятия, которое обслуживает обедов для различных клиентов. Другими словами, кейтеринговая компания может иметь несколько фирм, которые принимают заказы от кейтеринга, и она должна знать, сколько обедов она должна производить каждый день для всех своих клиентов!
Замечу, что я не использовал сортировку, чтобы не усложнять этот пример.
Это мой код:
@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)); } }
источник
Вот как я сделал группировку по нескольким полям branchCode и prdId, просто отправив это кому-то в нужде
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); } }
Результат выглядит следующим образом:
+++++++++++++++++++++++ [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}]
После форматирования:
[ 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} ]
источник