नक्शे से अलग-अलग मानों के साथ मानचित्र कैसे बनाएं (और बाइनरीऑपरेटर का उपयोग करके सही कुंजी का उपयोग करें)?


13

मेरे पास एक नक्शा है Map<K, V>और मेरा लक्ष्य डुप्लिकेट किए गए मूल्यों को दूर करना है और Map<K, V>फिर से उसी संरचना को आउटपुट करना है। डुप्लिकेट मान पाए जाने पर, kदो मान ( k1और k1) में से एक कुंजी ( ) चुनी जानी चाहिए जो इन मूल्यों को रखती है, इस कारण से, BinaryOperator<K>देने kसे k1और मान k2उपलब्ध है।

उदाहरण इनपुट और आउटपुट:

// Input
Map<Integer, String> map = new HashMap<>();
map.put(1, "apple");
map.put(5, "apple");
map.put(4, "orange");
map.put(3, "apple");
map.put(2, "orange");

// Output: {5=apple, 4=orange} // the key is the largest possible

का उपयोग करते हुए मेरे प्रयास Stream::collect(Supplier, BiConsumer, BiConsumer)है थोड़ा बहुत अनाड़ी और जैसे परिवर्तनशील संचालन होता है Map::putऔर Map::removeजो मैं से बचने के लिए करना चाहते हैं:

// // the key is the largest integer possible (following the example above)
final BinaryOperator<K> reducingKeysBinaryOperator = (k1, k2) -> k1 > k2 ? k1 : k2;

Map<K, V> distinctValuesMap = map.entrySet().stream().collect(
    HashMap::new,                                                              // A new map to return (supplier)
    (map, entry) -> {                                                          // Accumulator
        final K key = entry.getKey();
        final V value = entry.getValue();
        final Entry<K, V> editedEntry = Optional.of(map)                       // New edited Value
            .filter(HashMap::isEmpty)
            .map(m -> new SimpleEntry<>(key, value))                           // If a first entry, use it
            .orElseGet(() -> map.entrySet()                                    // otherwise check for a duplicate
                    .stream() 
                    .filter(e -> value.equals(e.getValue()))
                    .findFirst()
                    .map(e -> new SimpleEntry<>(                               // .. if found, replace
                            reducingKeysBinaryOperator.apply(e.getKey(), key), 
                            map.remove(e.getKey())))
                    .orElse(new SimpleEntry<>(key, value)));                   // .. or else leave
        map.put(editedEntry.getKey(), editedEntry.getValue());                 // put it to the map
    },
    (m1, m2) -> {}                                                             // Combiner
);

क्या Collectorsएक Stream::collectकॉल के भीतर एक उपयुक्त संयोजन का उपयोग करके एक समाधान है (जैसे कि बिना परिवर्तन योग्य संचालन)?


2
" बेहतर " या " सर्वश्रेष्ठ " के लिए आपका मैट्रिक्स क्या है ? Ist के माध्यम से किया जाना चाहिए Stream?
तूरिंग85

यदि समान मान 2 कुंजी के साथ जुड़ा हुआ है, तो आप कैसे चुनते हैं कि कौन सी कुंजी बरकरार रखी गई है?
माइकल

आपके मामले में अपेक्षित आउटपुट क्या हैं?
YCF_L

1
@ Turing85: जैसा कि मैंने कहा। बेहतर या सबसे अच्छा जैसे परिवर्तनशील नक्शा विधियों में से कोई स्पष्ट उपयोग के साथ होगा Map::putया Map::removeभीतर Collector
निकोलस

1
यह देखने लायक है BiMap। की शायद कोई डुप्लिकेट जावा में HashMap से निकालें डुप्लिकेट मानों
नमन

जवाबों:


12

आप संग्राहक.toMap का उपयोग कर सकते हैं

private Map<Integer, String> deduplicateValues(Map<Integer, String> map) {
    Map<String, Integer> inverse = map.entrySet().stream().collect(toMap(
            Map.Entry::getValue,
            Map.Entry::getKey,
            Math::max) // take the highest key on duplicate values
    );

    return inverse.entrySet().stream().collect(toMap(Map.Entry::getValue, Map.Entry::getKey));
}

9

इसे आज़माएं: सरल तरीका कुंजी और मूल्य को उलटा कर देता है फिर toMap()मर्ज फ़ंक्शन के साथ कलेक्टर का उपयोग करें ।

map.entrySet().stream()
        .map(entry -> new AbstractMap.SimpleEntry<>(entry.getValue(), entry.getKey()))
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, reducingKeysBinaryOperator));

Map<K, V> output = map.entrySet().stream()
        .collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey, reducingKeysBinaryOperator))
        .entrySet().stream()
        .collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));

2
मैं यह देखने में विफल हूं कि मध्यवर्ती mapऑपरेशन क्या खरीदता है। आप कुंजियों और मूल्यों को स्वैप करते हैं, यह बहुत स्पष्ट है, लेकिन क्या बात है, आप केवल एक ही कदम पर ऐसा कर सकते हैं?
जीपीआई

3
@ जीपीआई और माइकल, यह इसलिए है क्योंकि उसे चाबियों को मर्ज करना है, इसलिए जोड़े को कुंजियों को मर्ज करना होगा। फिर क्या है यह दूसरा उलटा है।
जीन-बैप्टिस्ट युनुस

2
@ हदीज न! उलटा सही था! लेकिन एक दूसरे को वापस पाने के लिए आवश्यक था। मर्ज का उपयोग कुंजियों को मर्ज करने के लिए किया जाता है, लेकिन विलय केवल मूल्यों के लिए ही संभव है ...
जीन-बैप्टिस्ट यूनेसे

@ जीन-बैप्टिस्टयूनस मैं विलय करने की आवश्यकता को समझता हूं, लेकिन मुझे तुरंत नहीं मिलता कि आप swap(); collect(key, value, binOp);इसके बजाय कोड क्यों हैं collect(value, key, binOp)। शायद मैं असली के लिए एक jshell में यह कोशिश करने की जरूरत है?
जीपीआई

2
आपके द्वारा साझा किए गए कोड में प्रश्न में पेश किए गए स्थानीय चर का उपयोग करने की स्वतंत्रता ली। यदि आप जवाब दे रहे थे तो मामले में उल्टा कर दें।
नमन

4

मुझे गैर-स्ट्रीम समाधान अधिक अभिव्यंजक लगता है:

BinaryOperator<K> reducingKeysBinaryOperator = (k1, k2) -> k1 > k2 ? k1 : k2;

Map<V, K> reverse = new LinkedHashMap<>(map.size());
map.forEach((k, v) -> reverse.merge(v, k, reducingKeysBinaryOperator));

Map<K, V> result = new LinkedHashMap<>(reverse.size());
reverse.forEach((v, k) -> result.put(k, v));

यह Map.mergeआपके द्वि-फ़ंक्शन को कम करने के साथ उपयोग करता है और LinkedHashMapमूल प्रविष्टियों के आदेश को संरक्षित करने के लिए उपयोग करता है।


2
हां, मैंने इसका (समान) समाधान निकाला है। हालांकि, मैं जावा-स्ट्रीम दृष्टिकोण की तलाश कर रहा हूं , क्योंकि यह अधिक घोषणात्मक तरीका है। मेरे +1
निकोलस

1

मुझे Collectorsफिर से लौटे हुए मानचित्र को एकत्र करने और आगे की प्रक्रिया के लिए बिना किसी आवश्यकता के उपयोग का एक तरीका मिल गया। विचार यह है:

  1. समूह Map<K, V>के लिए Map<V, List<K>

    Map<K, V> distinctValuesMap = this.stream.collect(
        Collectors.collectingAndThen(
            Collectors.groupingBy(Entry::getValue),
            groupingDownstream 
        )
    );

    {सेब = [१, ५, ३], नारंगी = [४, २]}

  2. उपयोग List<K>करने के लिए नई कुंजी ( ) को कम करें ।KBinaryOperator<K>

    Function<Entry<V, List<Entry<K, V>>>, K> keyMapFunction = e -> e.getValue().stream()
        .map(Entry::getKey)
        .collect(Collectors.collectingAndThen(
            Collectors.reducing(reducingKeysBinaryOperator),
            Optional::get
        )
    );

    {सेब = ५, नारंगी = ४}

  3. Map<V, K>वापस Map<K, V>संरचना में फिर से उलटा करें - जो कि सुरक्षित है क्योंकि दोनों कुंजी और मान अलग-अलग हैं।

    Function<Map<V, List<Entry<K,V>>>, Map<K, V>> groupingDownstream = m -> m.entrySet()
        .stream()
        .collect(Collectors.toMap(
            keyMapFunction,
            Entry::getKey
        )
    );

    {५ = सेब, ४ = नारंगी}

अंतिम कोड:

final BinaryOperator<K> reducingKeysBinaryOperator = ...

final Map<K, V> distinctValuesMap = map.entrySet().stream().collect(
        Collectors.collectingAndThen(
            Collectors.groupingBy(Entry::getValue),
            m -> m.entrySet().stream().collect(
                Collectors.toMap(
                    e -> e.getValue().stream().map(Entry::getKey).collect(
                        Collectors.collectingAndThen(
                            Collectors.reducing(reducingKeysBinaryOperator),
                            Optional::get
                        )
                    ),
                    Entry::getKey
                )
            )
        )
    );

1

"स्ट्रीम और संग्राहक.ग्रुपिंगबी" के साथ वांछित परिणाम प्राप्त करने के लिए एक और दृष्टिकोण।

    map = map.entrySet().stream()
    .collect(Collectors.groupingBy(
            Entry::getValue,
            Collectors.maxBy(Comparator.comparing(Entry::getKey))
            )
    )
    .entrySet().stream()
    .collect(Collectors.toMap(
            k -> {
                return k.getValue().get().getKey();
            }, 
            Entry::getKey));
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.