2017-06-07 6 views
9

जब Collectors.toMap() के दौरान डुप्लिकेट कुंजी प्रविष्टि पाई जाती है, तो मर्ज फ़ंक्शन (o1, o2) कहा जाता है।संग्राहक.टैप विलय समारोह में कुंजी कैसे प्राप्त करें?

प्रश्न: मैं नकल के कारण कुंजी कैसे प्राप्त कर सकता हूं?

String keyvalp = "test=one\ntest2=two\ntest2=three"; 

Pattern.compile("\n") 
    .splitAsStream(keyval) 
    .map(entry -> entry.split("=")) 
    .collect(Collectors.toMap(
     split -> split[0], 
     split -> split[1], 
     (o1, o2) -> { 
      //TODO how to access the key that caused the duplicate? o1 and o2 are the values only 
      //split[0]; //which is the key, cannot be accessed here 
     }, 
    HashMap::new)); 
मर्ज समारोह मैं कुंजी अगर मैं मानचित्रण रद्द जिसके आधार पर निर्णय लेते हैं, या जारी रखने और उन मूल्यों के पर ले जाना चाहते हैं अंदर

+0

बाद में आप मर्ज किए गए मान फ़िल्टर कर सकते हैं, क्या वास्तव में उन्हें विलय करते समय फ़िल्टर करने की आवश्यकता है? –

+0

क्या आप एक उदाहरण दे सकते हैं? विलय के दौरान मुझे यह तय करना होगा कि कौन से मूल्य लेना है (ओ 1 या ओ 2)। निर्णय ** कुंजी ** पर किया जाना चाहिए। लेकिन कुंजी हमेशा दो बार नहीं होती है।केवल कभी-कभी जिस मामले में विलय का निर्णय लिया जाना चाहिए। – membersound

+1

ठीक है मैं देखता हूँ। आप अलग नक्शा बना सकते हैं और '.map (एंट्री -> entry.split (" = ")) चला सकते हैं। प्रत्येक()' के लिए और प्रत्येक प्रविष्टि की जांच करें यदि मान पहले से ही मानचित्र में है। यदि नहीं - जोड़ें, अन्य - जांचें कि क्या प्रतिस्थापित करें या नहीं। –

उत्तर

5

आपको एक कस्टम कलेक्टर का उपयोग करने या एक अलग दृष्टिकोण का उपयोग करने की आवश्यकता है।

Map<String, String> map = new Hashmap<>(); 
Pattern.compile("\n") 
    .splitAsStream(keyval) 
    .map(entry -> entry.split("=")) 
    .forEach(arr -> map.merge(arr[0], arr[1], (o1, o2) -> /* use arr[0])); 

एक कस्टम कलेक्टर लिखना और अधिक जटिल है। आपको एक TriConsumer (कुंजी और दो मान) की आवश्यकता है जो जेडीके में नहीं है, इसलिए मुझे पूरा यकीन है कि फ़ंक्शन में कोई भी अंतर्निहित कार्य नहीं है। ;)

4

मर्ज फ़ंक्शन में कुंजी प्राप्त करने का कोई मौका नहीं है, जो एक ही समस्या है, बिल्टिन फ़ंक्शन में, जब आप मर्ज फ़ंक्शन को छोड़ देते हैं।

public static <T, K, V> Collector<T, ?, Map<K,V>> 
    toMap(Function<? super T, ? extends K> keyMapper, 
      Function<? super T, ? extends V> valueMapper) { 
    return Collector.of(HashMap::new, 
     (m, t) -> { 
      K k = keyMapper.apply(t); 
      V v = Objects.requireNonNull(valueMapper.apply(t)); 
      if(m.putIfAbsent(k, v) != null) throw duplicateKey(k, m.get(k), v); 
     }, 
     (m1, m2) -> { 
      m2.forEach((k,v) -> { 
       if(m1.putIfAbsent(k, v)!=null) throw duplicateKey(k, m1.get(k), v); 
      }); 
      return m1; 
     }); 
} 
private static IllegalStateException duplicateKey(Object k, Object v1, Object v2) { 
    return new IllegalStateException("Duplicate key "+k+" (values "+v1+" and "+v2+')'); 
} 

(यह मूलतः है क्या किसी मर्ज समारोह के बिना toMap के जावा 9 के कार्यान्वयन करना होगा)

:

समाधान एक अलग toMap कार्यान्वयन, जो Map.merge पर निर्भर नहीं करता उपयोग करने के लिए है

तो आपको अपने कोड में करने की ज़रूरत है, toMap कॉल को रीडायरेक्ट करना है और मर्ज फ़ंक्शन को छोड़ना है:

String keyvalp = "test=one\ntest2=two\ntest2=three"; 

Map<String, String> map = Pattern.compile("\n") 
     .splitAsStream(keyvalp) 
     .map(entry -> entry.split("=")) 
     .collect(toMap(split -> split[0], split -> split[1])); 

(या ContainingClass.toMap अगर इसकी न तो एक ही कक्षा और न ही स्थिर आयात में) < \ sup>

कलेक्टर, मूल toMap कलेक्टर की तरह समानांतर प्रसंस्करण का समर्थन करता है, हालांकि यह बहुत यहाँ समानांतर प्रसंस्करण से एक लाभ प्राप्त होने की संभावना नहीं है , प्रक्रिया के लिए और भी तत्वों के साथ।

हैं, अगर मैं आप सही तरीके से मिलता है, आप केवल या तो, पुराने या नए मूल्य लेने के लिए, मर्ज वास्तविक कुंजी के आधार पर समारोह में चाहते हैं, तो आप इसे एक महत्वपूर्ण Predicate इस

public static <T, K, V> Collector<T, ?, Map<K,V>> 
    toMap(Function<? super T, ? extends K> keyMapper, 
      Function<? super T, ? extends V> valueMapper, 
      Predicate<? super K> useOlder) { 
    return Collector.of(HashMap::new, 
     (m, t) -> { 
      K k = keyMapper.apply(t); 
      m.merge(k, valueMapper.apply(t), (a,b) -> useOlder.test(k)? a: b); 
     }, 
     (m1, m2) -> { 
      m2.forEach((k,v) -> m1.merge(k, v, (a,b) -> useOlder.test(k)? a: b)); 
      return m1; 
     }); 
} 
तरह के साथ कर सकता
Map<String, String> map = Pattern.compile("\n") 
     .splitAsStream(keyvalp) 
     .map(entry -> entry.split("=")) 
     .collect(toMap(split -> split[0], split -> split[1], key -> condition)); 

इस कलेक्टर अनुकूलित करने के लिए कई तरीके हैं ...

+1

मुझे नहीं पता कि आप इसे कितनी बार सुनते हैं, लेकिन धन्यवाद ... – Eugene

0

ज़ाहिर है, नहीं है, सरल और तुच्छ चाल - 'कुंजी नक्शाकार' समारोह में महत्वपूर्ण बचत और 'मर्ज' में महत्वपूर्ण हो रही समारोह। तो, कोड निम्न की तरह दिखेगा (मान लीजिए कि कुंजी इंटीजर है):

final AtomicInteger key = new AtomicInteger(); 
...collect(Collectors.toMap( 
    item -> { key.set(item.getKey()); return item.getKey(); }, // key mapper 
    item -> ..., // value mapper 
    (v1, v2) -> { log(key.get(), v1, v2); return v1; } // merge function 
); 

नोट: यह समांतर प्रसंस्करण के लिए अच्छा नहीं है।

संबंधित मुद्दे