2017-12-07 25 views
5

에서 오브젝트의 Map을 생성해야하는 경우가 종종 있습니다. 키는 일반적으로 일부 문자열 또는 열거 형 등이며 값은 데이터가 함께 묶여있는 새로운 개체입니다. 이 작업을 수행하는 일반적인 방법은 내 편이면 먼저 Map<String, SomeKeyValueObject>을 만든 다음 Set 또는 List를 반복하여 새로 만든 맵을 가져 와서 변경하는 것입니다. 다음의 예처럼Java 스트림 : 맵에 추가하지만 돌연변이는 피하십시오

:

class Example { 
    Map<String, GroupedDataObject> groupData(final List<SomeData> list){ 
     final Map<String, GroupedDataObject> map = new HashMap<>(); 
     for(final SomeData data : list){ 
     final String key = data.valueToGroupBy(); 
     map.put(key, GroupedDataObject.of(map.get(key), data.displayName(), data.data())); 
     } 
     return map; 
    } 

} 

class SomeData { 
    private final String valueToGroupBy; 
    private final Object data; 
    private final String displayName; 

    public SomeData(final String valueToGroupBy, final String displayName, final Object data) { 
     this.valueToGroupBy = valueToGroupBy; 
     this.data = data; 
     this.displayName = displayName; 
    } 

    public String valueToGroupBy() { 
     return valueToGroupBy; 
    } 

    public Object data() { 
     return data; 
    } 

    public String displayName() { 
     return displayName; 
    } 
} 

class GroupedDataObject{ 

    private final String key; 
    private final List<Object> datas; 

    private GroupedDataObject(final String key, final List<Object> list) { 
     this.key = key; 
     this.datas = list; 
    } 

    public static GroupedDataObject of(final GroupedDataObject groupedDataObject, final String key, final Object data) { 
     final List<Object> list = new ArrayList<>(); 
     if(groupedDataObject != null){ 
     list.addAll(groupedDataObject.datas()); 
     } 
     list.add(data); 
     return new GroupedDataObject(key, list); 
    } 

    public String key() { 
     return key; 
    } 

    public List<Object> datas() { 
     return datas; 
    } 
} 

이 아주 더러운 느낌. 우리는지도를 만든 다음 그것을 계속해서 변경합니다.

나는 자바 8s에서 Stream을 사용하고 돌연변이가없는 데이터 구조를 만들었다 (또는 오히려 돌연변이가 보이지 않는다). 따라서이 데이터 그룹을 명령형보다는 선언적 방식을 사용하는 것으로 전환하는 방법이 있습니까?

나는 https://stackoverflow.com/a/34453814/3478016에있는 제안을 실행하는 것을 시도했다 그러나 나는 비틀 거리고있는 것을 보인다. 대답의 접근법 (Collectors.groupingByCollectors.mapping의 제안)을 사용하여 데이터를지도로 정렬 할 수 있습니다. 그러나 "데이터"를 하나의 동일한 객체로 그룹화 할 수는 없습니다.

선언적 방식으로이를 수행 할 수있는 방법이 있습니까? 아니면 명령을 고수해야합니까?

+0

가인가를 * 마지막 *'data.displayName()' 'data.valueToGroupBy()'각각에 대해'data.data()'? – Bohemian

+0

@Bohemian'data.displayName()'은 각'data.valueToGroupBy()'에 대해 동일합니다. 나의 현재 문제에서'valueToGroupBy'는 영어이고'displayName'는 스웨덴어입니다. 그러나 그들은 동일합니다. – Chewtoy

+1

그건 내 질문에 대답하지 않습니다. 예 또는 아니오 : 각 키의 마지막 데이터 만 출력 맵에 표시하려고합니까? – Bohemian

답변

5

대신 병합 함수와 함께 Collectors.toMap을 사용할 수 있습니다.

Map<String, GroupedDataObject> map = 
    list.stream() 
     .collect(Collectors.toMap(SomeData::valueToGroupBy, 
            d -> { 
            List<Object> l = new ArrayList<>(); 
            l.add(d.data()); 
            return new GroupedDataObject(d.valueToGroupBy(), l); 
            }, 
            (g1,g2) -> { 
             g1.datas().addAll(g2.datas()); 
             return g1; 
            })); 

GroupedDataObject 생성자는이 작업을하기 위해 접근해야합니다.

+1

나는 이것이 컴파일 될 것이라고 생각하지 않는다. –

+0

이것은 내 문제를 완벽하게 해결했습니다. 줄이 길어 지지만 람다를 없애면 훨씬 더 재사용 할 수 있습니다. 나는 또한 생성자가 접근 할 필요가 없다는 것을 발견했다. 정적 팩토리 메서드를 사용할 수도 있습니다. – Chewtoy

+0

@Eran은 원래 그대로입니다. 그러나 약간의 재 작성만으로 작동합니다. value-lambda에서 사용하기 위해'String'과'Object'를 취하는'of' 중 하나,'String'을 취하는'of'과 merge-lambda에서 사용할 두번째'Set '. – Chewtoy

1

GroupedDataObject을 피하고 키와 목록이있는지도를 원하면 Collectors.groupingBy을 사용할 수 있습니다.

Collectors.groupingBy는이 작업을 수행 할 수 있습니다 :

List<SomeObject> list = getSomeList(); 

Map<SomeKey, List<SomeObject>> = list.stream().collect(Collectors.groupingBy(SomeObject::getKeyMethod)); 

equals

1

hashValue의 적절한 구현을 가지고 SomeKey이 필요합니다 때로는 스트림이 이동하는 방법이 없습니다. 나는 이것이 그 시대의 하나라고 생각한다.

merge()를 사용하여 약간의 리팩토링은 당신에게 제공합니다

Map<String, MyTuple> groupData(final List<SomeData> list) { 
    Map<String, MyTuple> map = new HashMap<>(); 
    list.forEach(d -> map.merge(d.valueToGroupBy(), new MyTuple(data.displayName(), data.data()), 
     (a, b) -> {a.addAll(b.getDatas()); return a;}); 

이 당신의 물건을 잡고 합리적인 클래스를 가정 : 당신은 단지 원하는

class MyTuple { 
    String displayName; 
    List<Object> datas = new ArrayList<>(); 
    // getters plus constructor that takes 1 data and adds it to list 
}