জাভা 8: স্ট্রিম / ম্যাপ-হ্রাস / সংগ্রাহক ব্যবহার করে হ্যাশম্যাপ <এক্স, ওয়াই] থেকে হ্যাশম্যাপ <এক্স, জেড>


209

আমি জানি কিভাবে "রুপান্তর" এটি একটি সহজ জাভা Listথেকে Y-> Z, অর্থাত্:

List<String> x;
List<Integer> y = x.stream()
        .map(s -> Integer.parseInt(s))
        .collect(Collectors.toList());

এখন আমি ম্যাপের সাথে মূলত একই কাজটি করতে চাই, অর্থাত:

INPUT:
{
  "key1" -> "41",    // "41" and "42"
  "key2" -> "42      // are Strings
}

OUTPUT:
{
  "key1" -> 41,      // 41 and 42
  "key2" -> 42       // are Integers
}

সমাধানটি সীমাবদ্ধ করা উচিত নয় String-> IntegerListউপরের উদাহরণের মতো , আমি যে কোনও পদ্ধতিতে (বা নির্মাণকারী) কল করতে চাই।

উত্তর:


372
Map<String, String> x;
Map<String, Integer> y =
    x.entrySet().stream()
        .collect(Collectors.toMap(
            e -> e.getKey(),
            e -> Integer.parseInt(e.getValue())
        ));

এটি তালিকার কোডের মতো দুর্দান্ত নয়। আপনি কলটিতে নতুন Map.Entryগুলি তৈরি করতে পারবেন না map()যাতে কাজটি collect()কলটিতে মিশে যায় ।


59
আপনি প্রতিস্থাপন করতে পারেন e -> e.getKey()সঙ্গে Map.Entry::getKey। তবে এটি স্বাদ / প্রোগ্রামিং স্টাইলের বিষয়।
হোলগার

5
আসলে এটি পারফরম্যান্সের বিষয়, আপনার ল্যাম্বদা 'স্টাইল' থেকে কিছুটা উচ্চতর হওয়ার পরামর্শ দেওয়া হচ্ছে
জন বার্গিন

36

এখানে সোটিরিওস ডেলিমনোলিসের উত্তরের কিছু প্রকরণ রয়েছে , যা (+1) দিয়ে শুরু করা বেশ ভাল ছিল। নিম্নোক্ত বিবেচনা কর:

static <X, Y, Z> Map<X, Z> transform(Map<? extends X, ? extends Y> input,
                                     Function<Y, Z> function) {
    return input.keySet().stream()
        .collect(Collectors.toMap(Function.identity(),
                                  key -> function.apply(input.get(key))));
}

একটি দম্পতি এখানে পয়েন্ট। প্রথমে জেনেরিকসে ওয়াইল্ডকার্ড ব্যবহার করা হয়; এটি ফাংশনটিকে কিছুটা নমনীয় করে তোলে। একটি ওয়াইল্ডকার্ড প্রয়োজনীয় হবে যদি উদাহরণস্বরূপ, আপনি আউটপুট মানচিত্রে একটি চাবি রাখতে চান যা ইনপুট মানচিত্রের কীটির একটি সুপারক্লাস রয়েছে:

Map<String, String> input = new HashMap<String, String>();
input.put("string1", "42");
input.put("string2", "41");
Map<CharSequence, Integer> output = transform(input, Integer::parseInt);

(মানচিত্রের মানগুলির জন্য একটি উদাহরণ রয়েছে তবে এটি সত্যই স্বীকৃত এবং আমি স্বীকার করি যে ওয়াইয়ের জন্য সীমাবদ্ধ ওয়াইল্ডকার্ড থাকা কেবল প্রান্তের ক্ষেত্রে সহায়তা করে))

দ্বিতীয় পয়েন্টটি হ'ল ইনপুট মানচিত্রের উপর দিয়ে স্ট্রিমটি চালানোর পরিবর্তে entrySetআমি এটির উপর দিয়ে দৌড়েছি keySet। এটি কোডটিকে কিছুটা পরিষ্কার করে দেয়, আমি মনে করি মানচিত্রের প্রবেশের পরিবর্তে মানচিত্রের বাইরে মানগুলি আনতে হবে। ঘটনাচক্রে, প্রথমদিকে আমার key -> keyপ্রথম যুক্তি হিসাবে ছিল এবং এটি toMap()কোনও কারণে কোনও প্রকার অনুমানের ত্রুটিতে ব্যর্থ হয়েছিল। এটি (X key) -> keyযেমন কাজ করেছে তেমন পরিবর্তন করা Function.identity()

আরও একটি ভিন্নতা নিম্নরূপ:

static <X, Y, Z> Map<X, Z> transform1(Map<? extends X, ? extends Y> input,
                                      Function<Y, Z> function) {
    Map<X, Z> result = new HashMap<>();
    input.forEach((k, v) -> result.put(k, function.apply(v)));
    return result;
}

এটি Map.forEach()স্ট্রিমের পরিবর্তে ব্যবহার করে। এটি এমনকি সহজ, আমি মনে করি, কারণ এটি সংগ্রহকারীদের সাথে বিতরণ করে, যা মানচিত্রের সাথে ব্যবহার করতে কিছুটা আনাড়ি। কারণটি হ'ল Map.forEach()পৃথক প্যারামিটার হিসাবে কী এবং মান দেয়, যেখানে স্ট্রিমটির একটি মাত্র মান থাকে - এবং আপনাকে কী বা মানটি মান হিসাবে প্রবেশদ্বারটি ব্যবহার করতে হবে তা বেছে নিতে হবে। বিয়োগের দিকে, এটিতে অন্যান্য পদ্ধতির সমৃদ্ধ, স্ট্রিম ভালির অভাব রয়েছে। :-)


11
Function.identity()দেখতে দেখতে দুর্দান্ত লাগছে তবে যেহেতু প্রথম সমাধানটিতে প্রতিটি প্রবেশের জন্য একটি মানচিত্র / হ্যাশ অনুসন্ধান প্রয়োজন যদিও অন্য সমস্ত সমাধানগুলি না করে, আমি এটির প্রস্তাব দেব না।
হোলগার

13

যেমন একটি জেনেরিক সমাধান

public static <X, Y, Z> Map<X, Z> transform(Map<X, Y> input,
        Function<Y, Z> function) {
    return input
            .entrySet()
            .stream()
            .collect(
                    Collectors.toMap((entry) -> entry.getKey(),
                            (entry) -> function.apply(entry.getValue())));
}

উদাহরণ

Map<String, String> input = new HashMap<String, String>();
input.put("string1", "42");
input.put("string2", "41");
Map<String, Integer> output = transform(input,
            (val) -> Integer.parseInt(val));

জেনেরিকগুলি ব্যবহার করে দুর্দান্ত পন্থা। আমি মনে করি যদিও এটি কিছুটা উন্নতি করা যায় - আমার উত্তর দেখুন।
স্টুয়ার্ট

13

পেয়ারার কাজটি Maps.transformValuesআপনি যা সন্ধান করছেন তা হ'ল ল্যাম্বডা ভাবের সাথে এটি দুর্দান্তভাবে কাজ করে:

Maps.transformValues(originalMap, val -> ...)

আমি এই পদ্ধতির পছন্দ করি তবে এটিকে java.util.Function না দিয়ে সতর্ক হন be যেহেতু এটি com.google.common.base.Function প্রত্যাশা করে, তাই গ্রহগ্রহ একটি অস্বাস্থ্যকর ত্রুটি দেয় - এটি বলে যে ফাংশন ফাংশনের জন্য প্রযোজ্য নয়, যা বিভ্রান্তিকর হতে পারে: "পদ্ধতিটি রূপান্তরকরণের মান (মানচিত্র <কে, ভি 1>, ফাংশন <? সুপার ভি 1) , ভি 2>) ধরণের মানচিত্র আর্গুমেন্টের জন্য প্রযোজ্য নয় (মানচিত্র <ফু, বার>, ফাংশন <বার, বাজ>) "
এমএসকিফিশার 13 '16 এ 13

যদি আপনার অবশ্যই পাস হয় তবে java.util.Functionআপনার কাছে দুটি বিকল্প রয়েছে। 1. জাভা টাইপ অনুমানটি এটি চিত্রিত করতে ল্যাম্বডা ব্যবহার করে সমস্যাটি এড়িয়ে চলুন। ২. javaFunction :: এর মতো কোনও পদ্ধতি রেফারেন্স ব্যবহার করুন এমন একটি নতুন ল্যাম্বদা উত্পাদন করতে প্রয়োগ করুন যা টাইপ অনুমানটি নির্ধারণ করতে পারে।
জো

10

এটি কি পুরোপুরি 100% কার্যকরী এবং সাবলীল হতে হবে? যদি তা না হয় তবে এটি কীভাবে হবে, এটি যতটা সংক্ষিপ্ত আকারে এটি পায়:

Map<String, Integer> output = new HashMap<>();
input.forEach((k, v) -> output.put(k, Integer.valueOf(v));

( যদি আপনি পার্শ্ব প্রতিক্রিয়াগুলির সাথে স্ট্রিমগুলির সংমিশ্রণের লজ্জা এবং অপরাধবোধের সাথে বাঁচতে পারেন )


5

আমার স্ট্রিমেক্স লাইব্রেরি যা স্ট্যান্ডার্ড স্ট্রিম এপিআই বাড়ায় এমন একটি EntryStreamবর্গ সরবরাহ করে যা মানচিত্রকে রূপান্তর করার জন্য আরও ভাল :

Map<String, Integer> output = EntryStream.of(input).mapValues(Integer::valueOf).toMap();

4

একটি বিকল্প যে সবসময় উদ্দেশ্য শেখার জন্য বিদ্যমান Collector.of () মাধ্যমে আপনার কাস্টম কালেক্টর গড়ে তুলতে যদিও toMap () JDK সংগ্রাহক এখানে (+1 টি সংক্ষিপ্ত হয় এখানে )।

Map<String,Integer> newMap = givenMap.
                entrySet().
                stream().collect(Collector.of
               ( ()-> new HashMap<String,Integer>(),
                       (mutableMap,entryItem)-> mutableMap.put(entryItem.getKey(),Integer.parseInt(entryItem.getValue())),
                       (map1,map2)->{ map1.putAll(map2); return map1;}
               ));

আমি এই কাস্টম সংগ্রাহককে একটি বেস হিসাবে শুরু করেছি এবং এটি যুক্ত করতে চেয়েছিলাম, অন্তত প্রবাহ () এর পরিবর্তে সমান্তরাল স্ট্রিম () ব্যবহার করার সময়, বাইনারি অপারেটরটির সাথে আরও কিছু মিলিয়ে পুনরায় লেখা উচিত বা হ্রাসকালে মানগুলি হারাতে map2.entrySet().forEach(entry -> { if (map1.containsKey(entry.getKey())) { map1.get(entry.getKey()).merge(entry.getValue()); } else { map1.put(entry.getKey(),entry.getValue()); } }); return map1হবে।
ব্যবহারকারী 691154

3

যদি আপনি তৃতীয় পক্ষের গ্রন্থাগারগুলি ব্যবহার করতে আপত্তি করেন না তবে আমার সাইক্লোপস-রিঅ্যাক্ট লাইবটিতে মানচিত্র সহ সমস্ত জেডিকে সংগ্রহের ধরণের জন্য এক্সটেনশন রয়েছে । আমরা কেবল 'মানচিত্র' অপারেটরটি ব্যবহার করে মানচিত্রটি রূপান্তর করতে পারি (ডিফল্ট মানচিত্রে মানচিত্রে মানগুলিতে কাজ করে)।

   MapX<String,Integer> y = MapX.fromMap(HashMaps.of("hello","1"))
                                .map(Integer::parseInt);

বিম্যাপটি একই সাথে কী এবং মানগুলিকে রূপান্তর করতে ব্যবহার করা যেতে পারে

  MapX<String,Integer> y = MapX.fromMap(HashMaps.of("hello","1"))
                               .bimap(this::newKey,Integer::parseInt);

0

ঘোষণামূলক এবং সহজ সমাধানটি হ'ল:

yourMutableMap.replaceAll ((কী, ভাল) -> রিটার্ন_ভ্যালু_ও_বিবি_ আপনার_ফানশন); নম্বর। আপনার মানচিত্রের স্থিতিটি পরিবর্তন করে রাখুন aware সুতরাং এটি আপনি যা চান তা নাও হতে পারে।

চিয়ার্স: http://www.deadcoderising.com/2017-02-14-java-8-declarative-ways-of-modifying-a-map-using-compute- विसरा- and-replace /

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.