2017-12-07 54 views
5

मुझे अक्सर ऐसी स्थिति में मिलती है जहां मुझे सेट या List से ऑब्जेक्ट्स का Map बनाना होगा। कुंजी आमतौर पर कुछ स्ट्रिंग या एनम या पसंद होती है, और मान कुछ नया ऑब्जेक्ट होता है जिसमें डेटा एक साथ लम्बा होता है। ऐसा करने का सामान्य तरीका, मेरे भाग के लिए, पहले Map<String, SomeKeyValueObject> बना रहा है और उसके बाद सेट या सूची में पुनरावृत्ति करना है और मैं अपने नए बनाए गए मानचित्र को बदलता हूं।जावा स्ट्रीम: मानचित्र में जोड़ें लेकिन उत्परिवर्तन से बचें

निम्न उदाहरण जैसा

:

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.groupingBy और Collectors.mapping का उपयोग करने का सुझाव) मैं मानचित्र में क्रमबद्ध डेटा प्राप्त करने में सक्षम हूं। लेकिन मैं "डेटा" को एक और एक ही वस्तु में समूहित नहीं कर सकता।

क्या घोषणात्मक तरीके से ऐसा करने का कोई तरीका है, या क्या मैं अनिवार्य रूप से अटक गया हूं?

+0

यह है कि आप केवल चाहते हैं * पिछले * 'data.displayName()' और 'data.data()' प्रत्येक 'data.valueToGroupBy()' के लिए? – Bohemian

+0

@ बोहेमियन 'data.displayName() 'प्रत्येक' data.valueToGroupBy() 'के लिए समान है। मेरी वर्तमान समस्या में, 'valueToGroupBy' अंग्रेजी में है, और 'displayName' स्वीडिश में है। लेकिन वे वही हैं। – Chewtoy

+1

यह मेरे प्रश्न का उत्तर नहीं देता है। हां या नहीं: क्या आप आउटपुट मानचित्र में प्रत्येक कुंजी के लिए केवल अंतिम डेटा डालने की कोशिश कर रहे हैं? – Bohemian

उत्तर

5

Collectors.groupingBy के बजाय आप मर्ज फ़ंक्शन के साथ 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 जैसा नहीं था। लेकिन इसके बारे में थोड़ा पुनर्लेखन के साथ यह काम करता है। मर्ज-लैम्ब्डा में उपयोग करने के लिए मूल्य-लैम्ब्डा में उपयोग करने के लिए 'स्ट्रिंग' और 'ऑब्जेक्ट' में से एक को '' '' लेना, और' 'स्ट्रिंग 'और दो' सेट 'लेना। – Chewtoy

1

यदि आप GroupedDataObject से बचें और बस एक कुंजी और एक सूची के साथ एक नक्शा चाहते हैं तो आप Collectors.groupingBy का उपयोग कर सकते हैं जिसे आप देख रहे हैं। कभी कभी धाराओं जाने का रास्ता नहीं हैं

List<SomeObject> list = getSomeList(); 

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

यह equals और hashValue

1

के समुचित कार्यान्वयन के लिए SomeKey की आवश्यकता होगी:

Collectors.groupingBy आप ऐसा करने की अनुमति देगा। मेरा मानना ​​है कि यह उन समयों में से एक है।

एक छोटी सी 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 
} 
संबंधित मुद्दे