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에서 둘 이상의 필드로 그룹화 할 수있는 다른 옵션이 있습니까?
예를 들어 제 경우에는 이름과 나이별로 그룹화하고 싶습니다.
답변
여기에 몇 가지 옵션이 있습니다. 가장 간단한 방법은 수집가를 연결하는 것입니다.
Map<String, Map<Integer, List<Person>>> map = people
.collect(Collectors.groupingBy(Person::getName,
Collectors.groupingBy(Person::getAge));
그런 다음 Fred라는 18 세 노인 목록을 얻으려면 다음을 사용합니다.
map.get("Fred").get(18);
두 번째 옵션은 그룹화를 나타내는 클래스를 정의하는 것입니다. 이것은 Person 내부에있을 수 있습니다. 이 코드는 a를 사용 record
하지만 JEP 359가 추가되기 전에 Java 버전에서 클래스 (포함 equals
및 hashCode
정의 됨)가 될 수 있습니다 .
class Person {
record NameAge(String name, int age) { }
public NameAge getNameAge() {
return new NameAge(name, age);
}
}
그런 다음 다음을 사용할 수 있습니다.
Map<NameAge, List<Person>> map = people.collect(Collectors.groupingBy(Person::getNameAge));
및 검색
map.get(new NameAge("Fred", 18));
마지막으로 자신의 그룹 레코드를 구현하지 않으려면 주변의 많은 Java 프레임 워크 pair
에 이러한 유형의 클래스를 위해 설계된 클래스 가 있습니다 . 예 : apache commons pair 이러한 라이브러리 중 하나를 사용하는 경우 맵에 대한 키를 이름과 연령 쌍으로 만들 수 있습니다.
Map<Pair<String, Integer>, List<Person>> map =
people.collect(Collectors.groupingBy(p -> Pair.of(p.getName(), p.getAge())));
다음으로 검색 :
map.get(Pair.of("Fred", 18));
개인적으로 저는 레코드가 의도를 더 잘 표시하고 코드가 거의 필요하지 않기 때문에 레코드를 언어로 사용할 수 있으므로 일반 튜플에서 많은 가치를 보지 못합니다.
답변
여기 코드를보세요 :
당신은 단순히 Function을 생성하고 그것이 당신을 위해 일하도록 할 수 있습니다. 일종의 기능적 스타일!
Function<Person, List<Object>> compositeKey = personRecord ->
Arrays.<Object>asList(personRecord.getName(), personRecord.getAge());
이제지도로 사용할 수 있습니다.
Map<Object, List<Person>> map =
people.collect(Collectors.groupingBy(compositeKey, Collectors.toList()));
건배!
답변
이 groupingBy
메소드에는 첫 번째 매개 변수가 있습니다 Function<T,K>
.
@param
<T>
입력 요소의 유형@param
<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());
}
}));
마지막으로 람다로 바꾼 후 코드는 다음과 같습니다.
Map<Pair<Integer,String>, List<Person>> peopleByAgeAndName = people.collect(Collectors.groupingBy(p -> Pair.of(person.getAge(), person.getName()), Collectors.mapping((Person p) -> p, toList())));
답변
안녕하세요 당신은 단순히 다음 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())));
답변
List를 여러 필드의 분류 자로 사용할 수 있지만 null 값을 Optional로 래핑해야합니다.
Function<String, List> classifier = (item) -> List.of(
item.getFieldA(),
item.getFieldB(),
Optional.ofNullable(item.getFieldC())
);
Map<List, List<Item>> grouped = items.stream()
.collect(Collectors.groupingBy(classifier));
답변
다양한 고객에게 점심을 제공하는 케이터링 회사에 보고서를 작성해야했습니다. 즉, 케이터링에는 케이터링에서 주문을받는 회사가 여러 개있을 수 있으며 모든 고객을 위해 매일 몇 개의 점심을 생산해야하는지 알아야합니다!
이 예제를 지나치게 복잡하게 만들지 않기 위해 정렬을 사용하지 않았습니다.
이것은 내 코드입니다.
@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));
}
}