J'ai trouvé le code permettant de regrouper les objets par un nom de champ provenant de POJO. Ci-dessous le code pour cela:
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);
}
}
Et le résultat est (ce qui est correct):
{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}]}
Mais que faire si je veux grouper par plusieurs champs? Je peux évidemment passer certains POJO dans la méthode groupingBy()
après avoir implémenté la méthode equals()
dans ce POJO, mais existe-t-il une autre option comme celle que je peux regrouper en plusieurs champs à partir du POJO donné?
Par exemple. Ici, dans mon cas, je veux regrouper par nom et par âge.
Vous avez quelques options ici. Le plus simple est de chaîner vos collectionneurs:
Map<String, Map<Integer, List<Person>>> map = people
.collect(Collectors.groupingBy(Person::getName,
Collectors.groupingBy(Person::getAge));
Ensuite, pour obtenir une liste de personnes de 18 ans appelée Fred, vous utiliseriez:
map.get("Fred").get(18);
Une deuxième option consiste à définir une classe représentant le groupement. Cela peut être à l'intérieur de la personne:
class Person {
public static class NameAge {
public NameAge(String name, int age) {
...
}
// must implement equals and hash function
}
public NameAge getNameAge() {
return new NameAge(name, age);
}
}
Ensuite, vous pouvez utiliser:
Map<NameAge, List<Person>> map = people.collect(Collectors.groupingBy(Person::getNameAge));
et rechercher avec
map.get(new NameAge("Fred", 18));
Enfin, si vous ne souhaitez pas implémenter votre propre classe de groupe, de nombreux frameworks Java ont une classe pair
conçue pour exactement ce type de chose. Par exemple: Apache commons pair Si vous utilisez l'une de ces bibliothèques, vous pouvez alors attribuer à la carte une clé du nom et de l'âge:
Map<Pair<String, Integer>, List<Person>> map =
people.collect(Collectors.groupingBy(p -> Pair.of(p.getName(), p.getAge())));
et récupérer avec:
map.get(Pair.of("Fred", 18));
Personnellement, je n'aime vraiment pas ces bibliothèques Tuple. Ils semblent être l’opposé exact du bon design OO: ils cachent l’intention au lieu de la révéler.
Cela dit, vous pouvez combiner les deux autres options en définissant votre propre classe de regroupement, mais en l'implémentant en étendant simplement Pair
, ce qui vous épargne une grande partie du travail de définition de equals
, etc. comme toute autre collection.
Bonne chance.
Ici, regardez le code:
Vous pouvez simplement créer une fonction et la laisser faire le travail pour vous, genre de style fonctionnel!
Function<Person, List<Object>> compositeKey = personRecord ->
Arrays.<Object>asList(personRecord.getName(), personRecord.getAge());
Maintenant, vous pouvez l'utiliser comme une carte:
Map<Object, List<Person>> map =
people.collect(Collectors.groupingBy(compositeKey, Collectors.toList()));
À votre santé!
Bonjour, vous pouvez simplement concaténer votre groupingByKey
telle que
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();
}
Le premier paramètre de la méthode groupingBy
est Function<T,K>
où:
@param
<T>
le type des éléments en entrée@param
<K>
le type des clés
Si nous remplaçons lambda par la classe anonyme dans votre code, nous pouvons en voir une sorte:
people.stream().collect(Collectors.groupingBy(new Function<Person, int>() {
@Override
public int apply(Person person) {
return person.getAge();
}
}));
Il suffit maintenant de modifier le paramètre de sortie<K>
. Dans ce cas, par exemple, j’ai utilisé une classe de paires de org.Apache.commons.lang3.Tuple pour regrouper par nom et par âge, mais vous pouvez créer votre propre classe pour filtrer les groupes selon vos besoins.
people.stream().collect(Collectors.groupingBy(new Function<Person, Pair<Integer, String>>() {
@Override
public YourFilter apply(Person person) {
return Pair.of(person.getAge(), person.getName());
}
}));
Enfin, après avoir remplacé par lambda back, le code ressemble à ceci:
Map<Pair<Integer,String>, List<Person>> peopleByAgeAndName = people.collect(Collectors.groupingBy(p -> Pair.of(person.getAge(), person.getName()), Collectors.mapping((Person p) -> p, toList())));
Définissez une classe pour la définition de clé dans votre groupe.
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
}
Maintenant dans votre code,
peopleByManyParams = people
.collect(Collectors.groupingBy(p -> new KeyObj( p.age, p.other1, p.other2 ), Collectors.mapping((Person p) -> p, toList())));
J'avais besoin de faire un rapport pour une entreprise de restauration qui sert des déjeuners pour divers clients. En d’autres termes, la restauration peut avoir plus d’une entreprise qui prend des commandes, et elle doit savoir combien de repas elle doit produire chaque jour pour tous ses clients!
Juste pour remarquer, je n'ai pas utilisé le tri, afin de ne pas trop compliquer cet exemple.
Ceci est mon code:
@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));
}
}