সংগ্রাহক। টোম্যাপে জাভা 8 নলপয়েন্টারএক্সসেপশন


331

জাভা 8 টি Collectors.toMapনম্বরের NullPointerExceptionমানগুলির একটি হলে নিক্ষেপ করে। আমি এই আচরণটি বুঝতে পারি না, মানচিত্রে কোনও সমস্যা ছাড়াই মান হিসাবে নাল পয়েন্টার থাকতে পারে। মানগুলি কেন বাতিল হতে পারে না এমন কোনও কারণ আছে Collectors.toMap?

এছাড়াও, এটি ঠিক করার কোনও দুর্দান্ত জাভা 8 উপায় আছে, বা লুপের জন্য আমার পুরানো প্লেইনে ফিরে যেতে হবে?

আমার সমস্যার উদাহরণ:

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


class Answer {
    private int id;

    private Boolean answer;

    Answer() {
    }

    Answer(int id, Boolean answer) {
        this.id = id;
        this.answer = answer;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public Boolean getAnswer() {
        return answer;
    }

    public void setAnswer(Boolean answer) {
        this.answer = answer;
    }
}

public class Main {
    public static void main(String[] args) {
        List<Answer> answerList = new ArrayList<>();

        answerList.add(new Answer(1, true));
        answerList.add(new Answer(2, true));
        answerList.add(new Answer(3, null));

        Map<Integer, Boolean> answerMap =
        answerList
                .stream()
                .collect(Collectors.toMap(Answer::getId, Answer::getAnswer));
    }
}

ক্ষেত্রে স্টেকট্র্যাস:

Exception in thread "main" java.lang.NullPointerException
    at java.util.HashMap.merge(HashMap.java:1216)
    at java.util.stream.Collectors.lambda$toMap$168(Collectors.java:1320)
    at java.util.stream.Collectors$$Lambda$5/1528902577.accept(Unknown Source)
    at java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
    at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1359)
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:512)
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:502)
    at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
    at Main.main(Main.java:48)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)

এই সমস্যাটি এখনও জাভা 11 এ বিদ্যমান।


5
nullTreeMap এর মতো সর্বদা কিছুটা সমস্যাযুক্ত ছিল। চেষ্টা করার জন্য একটি সুন্দর মুহূর্ত হতে পারে Optional<Boolean>? অন্যথায় বিভক্ত এবং ফিল্টার ব্যবহার করুন।
জোপ ডিম্বাণু

5
@ জোপএগজেন nullকোনও কীটির জন্য সমস্যা হতে পারে তবে এই ক্ষেত্রে এটির মান the
gontard

সব মানচিত্র সহ সমস্যা null, HashMapউদাহরণস্বরূপ এক হতে পারে nullকী এবং যে কোন সংখ্যার nullমান, আপনি একটি কাস্টম তৈরি করার চেষ্টা করতে পারেন Collectorব্যবহার করে একটি HashMapপরিবর্তে ডিফল্ট ব্যবহার।
kajacx

2
@ kajacx তবে ডিফল্ট বাস্তবায়নটি হ'ল স্ট্যাকট্রেসের HashMapপ্রথম লাইনে প্রদর্শিত। সমস্যাটি এমন নয় যে একটি মান Mapধরে রাখতে পারে না null, তবে Map#mergeফাংশনের দ্বিতীয় যুক্তিটি বাতিল হতে পারে না।
সিজারি

ব্যক্তিগতভাবে, প্রদত্ত পরিস্থিতিগুলির সাথে, আমি অ স্ট্রিম সমাধান বা ইনপুটটি সমান্তরাল হলে প্রতিটি () এর জন্য যাব। নীচে সুন্দর সংক্ষিপ্ত স্ট্রিম ভিত্তিক সমাধানগুলির একটি ভয়াবহ কর্মক্ষমতা থাকতে পারে।
ওন্দ্র Žižka

উত্তর:


301

আপনি এটি দিয়ে ওপেনজেডিকে এই পরিচিত বাগের চারপাশে কাজ করতে পারেন :

Map<Integer, Boolean> collect = list.stream()
        .collect(HashMap::new, (m,v)->m.put(v.getId(), v.getAnswer()), HashMap::putAll);

এটি খুব সুন্দর নয়, তবে এটি কাজ করে। ফলাফল:

1: true
2: true
3: null

( এই টিউটোরিয়ালটি আমাকে সবচেয়ে বেশি সাহায্য করেছে))


3
@ জাগার হ্যাঁ, সরবরাহকারী সংজ্ঞা (প্রথম যুক্তি) এমন একটি ফাংশন যা কোনও প্যারামিটারগুলি পাস করে না এবং ফলাফল দেয়, সুতরাং আপনার মামলার ল্যাম্বডাটি () -> new TreeMap<>(String.CASE_INSENSITIVE_ORDER)একটি সংবেদনশীল Stringকীড তৈরি করা হবে TreeMap
ব্রেট রায়ান 12

2
এটি সঠিক উত্তর এবং পরিবর্তে এর ডিফল্ট নন-ওভারলোড হওয়া সংস্করণের জন্য জেডিকে কী করা উচিত তা IMHO। সম্ভবত মার্জটি আরও দ্রুত, তবে আমি পরীক্ষা করিনি।
ব্রেট রায়ান 12

1
আমি কম্পাইল করার, যে ভাবে টাইপ পরামিতি উল্লেখ করার ছিল: Map<Integer, Boolean> collect = list.stream().collect(HashMap<Integer, Boolean>::new, (m,v)->m.put(v.getId(), v.getAnswer()), HashMap<Integer, Boolean>::putAll);। আমার ছিল:incompatible types: cannot infer type-variable(s) R (argument mismatch; invalid method reference no suitable method found for putAll(java.util.Map<java.lang.Integer,java.lang.Boolean>,java.util.Map<java.lang.Integer,java.lang.Boolean>) method java.util.Map.putAll(java.util.Map) is not applicable (actual and formal argument lists differ in length)
অ্যান্টনি ও।

2
বড় ইনপুটটিতে এটি বেশ ধীর হতে পারে। আপনি একটি তৈরি করেন HashMapএবং তারপরে putAll()প্রতিটি একক প্রবেশের জন্য কল করুন । ব্যক্তিগতভাবে, নির্দিষ্ট পরিস্থিতিতে আমি অ-স্ট্রিম সমাধানের সাথে যেতে পারি, বা forEach()যদি ইনপুটটি সমান্তরাল হয়।
ওন্দ্র ŽiŽka

3
এই দ্রবণটি মূল টু ম্যাপ প্রয়োগের চেয়ে আলাদা আচরণ করে বলে সাবধান হন। আসল বাস্তবায়ন সদৃশ কীগুলি সনাক্ত করে এবং একটি অবৈধ স্ট্যাটএক্সেপশন নিক্ষেপ করে, তবে এই সমাধানটি নীরবে সর্বশেষ কীটি গ্রহণ করে। এমানুয়েল Touzery এর সমাধান ( stackoverflow.com/a/32648397/471214 ) মূল আচরণ কাছাকাছি।
মিমডিমিরবাস

173

এর স্থির পদ্ধতি দ্বারা এটি সম্ভব নয় Collectors। এর জাভাদোক toMapযে toMapএর উপর ভিত্তি করে Map.merge:

@ পরিমাপ একত্রিত ফাংশনটি একটি সংযুক্তি ফাংশন, সরবরাহ করা হিসাবে একই কী সম্পর্কিত মানগুলির মধ্যে সংঘর্ষগুলি সমাধান করার জন্য ব্যবহৃত হয় Map#merge(Object, Object, BiFunction)}

এবং জাভাদোক Map.mergeবলেছেন:

@throws নালপয়েন্টারএক্সেপশন যদি নির্দিষ্ট কী নাল এবং এই মানচিত্র নাল কী সমর্থন করে না অথবা মান বা remappingFunction হয় নাল

আপনি forEachআপনার তালিকার পদ্ধতিটি ব্যবহার করে লুপটি এড়াতে পারেন ।

Map<Integer,  Boolean> answerMap = new HashMap<>();
answerList.forEach((answer) -> answerMap.put(answer.getId(), answer.getAnswer()));

তবে এটি পুরানো পদ্ধতির চেয়ে সত্যই সহজ নয়:

Map<Integer, Boolean> answerMap = new HashMap<>();
for (Answer answer : answerList) {
    answerMap.put(answer.getId(), answer.getAnswer());
}

3
সেক্ষেত্রে আমি বরং প্রত্যেকের জন্য পুরানো ধাঁচের ব্যবহার করব। আমি কি এটিকে টোগার্গে একটি বাগ বিবেচনা করব? যেহেতু এই মার্জ ফাংশনটির ব্যবহার সত্যিই একটি বাস্তবায়নের বিশদ, বা ম্যাপকে নাল মানগুলিতে প্রক্রিয়া না করার জন্য একটি ভাল যুক্তি?
জ্যাস্পার

6
এটা তোলে একত্রীকরণ এর javadoc উল্লেখ করা হয়, কিন্তু এটি বিবৃত করা হয় না toMap এর দস্তাবেজে অন্তর্ভুক্ত
জ্যাসপার

119
কখনই ভাবিনি যে মানচিত্রে নাল মানগুলি স্ট্যান্ডার্ড এপিআইতে এমন প্রভাব ফেলবে, আমি বরং এটিকে একটি ত্রুটি হিসাবে বিবেচনা করব।
আসকার কল্যাভক

16
আসলে এপিআই ডক্স ব্যবহার সম্পর্কে কিছু জানায় না Map.merge। এই আইএমএইচও বাস্তবায়নের একটি ত্রুটি যা একেবারেই গ্রহণযোগ্য ব্যবহারের ক্ষেত্রে সীমাবদ্ধ নয় যা উপেক্ষা করা হয়েছে। এর ওভারলোড পদ্ধতি toMapব্যবহার Do রাষ্ট্র Map.mergeনা কিন্তু এক op ব্যাবহার করে করা হয়।
ব্রেট রায়ান 12

11
@ জ্যাস্পারেও বাগ রিপোর্ট রয়েছে bugs.openjdk.java.net/browse/JDK-8148463
পিক্সেল

23

আমি একটি লিখেছিলাম Collector, যা পূর্বনির্ধারিত জাভা-এর মতো নয়, যখন আপনার nullমান থাকে তখন ক্রাশ হয় না :

public static <T, K, U>
        Collector<T, ?, Map<K, U>> toMap(Function<? super T, ? extends K> keyMapper,
                Function<? super T, ? extends U> valueMapper) {
    return Collectors.collectingAndThen(
            Collectors.toList(),
            list -> {
                Map<K, U> result = new HashMap<>();
                for (T item : list) {
                    K key = keyMapper.apply(item);
                    if (result.putIfAbsent(key, valueMapper.apply(item)) != null) {
                        throw new IllegalStateException(String.format("Duplicate key %s", key));
                    }
                }
                return result;
            });
}

এই ফাংশনটিতে কেবলমাত্র আপনার Collectors.toMap()কলকে প্রতিস্থাপন করুন এবং এটি সমস্যার সমাধান করবে।


1
তবে nullমানগুলিকে অনুমতি দেওয়া এবং ব্যবহার করা putIfAbsentএকসাথে ভাল খেলেন না। এটা তোলে ডুপ্লিকেট চাবি শনাক্ত করতে পারছে না যখন তারা ম্যাপ null...
হোলগার

10

হ্যাঁ, আমার কাছ থেকে দেরি করা উত্তর, তবে আমি মনে করি যে কেউ যদি অন্য কিছু- Collectorলজিক কোড করতে চান তবে হুডের নীচে কী ঘটছে তা বুঝতে সহায়তা করতে পারে ।

আমি আরও দেশীয় এবং সরাসরি এগিয়ে যাওয়ার পদ্ধতির কোডিং করে সমস্যাটি সমাধান করার চেষ্টা করেছি। আমি মনে করি এটি যথাসম্ভব সরাসরি:

public class LambdaUtilities {

  /**
   * In contrast to {@link Collectors#toMap(Function, Function)} the result map
   * may have null values.
   */
  public static <T, K, U, M extends Map<K, U>> Collector<T, M, M> toMapWithNullValues(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper) {
    return toMapWithNullValues(keyMapper, valueMapper, HashMap::new);
  }

  /**
   * In contrast to {@link Collectors#toMap(Function, Function, BinaryOperator, Supplier)}
   * the result map may have null values.
   */
  public static <T, K, U, M extends Map<K, U>> Collector<T, M, M> toMapWithNullValues(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, Supplier<Map<K, U>> supplier) {
    return new Collector<T, M, M>() {

      @Override
      public Supplier<M> supplier() {
        return () -> {
          @SuppressWarnings("unchecked")
          M map = (M) supplier.get();
          return map;
        };
      }

      @Override
      public BiConsumer<M, T> accumulator() {
        return (map, element) -> {
          K key = keyMapper.apply(element);
          if (map.containsKey(key)) {
            throw new IllegalStateException("Duplicate key " + key);
          }
          map.put(key, valueMapper.apply(element));
        };
      }

      @Override
      public BinaryOperator<M> combiner() {
        return (left, right) -> {
          int total = left.size() + right.size();
          left.putAll(right);
          if (left.size() < total) {
            throw new IllegalStateException("Duplicate key(s)");
          }
          return left;
        };
      }

      @Override
      public Function<M, M> finisher() {
        return Function.identity();
      }

      @Override
      public Set<Collector.Characteristics> characteristics() {
        return Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH));
      }

    };
  }

}

এবং JUnit এবং assertj ব্যবহার করে পরীক্ষাগুলি:

  @Test
  public void testToMapWithNullValues() throws Exception {
    Map<Integer, Integer> result = Stream.of(1, 2, 3)
        .collect(LambdaUtilities.toMapWithNullValues(Function.identity(), x -> x % 2 == 1 ? x : null));

    assertThat(result)
        .isExactlyInstanceOf(HashMap.class)
        .hasSize(3)
        .containsEntry(1, 1)
        .containsEntry(2, null)
        .containsEntry(3, 3);
  }

  @Test
  public void testToMapWithNullValuesWithSupplier() throws Exception {
    Map<Integer, Integer> result = Stream.of(1, 2, 3)
        .collect(LambdaUtilities.toMapWithNullValues(Function.identity(), x -> x % 2 == 1 ? x : null, LinkedHashMap::new));

    assertThat(result)
        .isExactlyInstanceOf(LinkedHashMap.class)
        .hasSize(3)
        .containsEntry(1, 1)
        .containsEntry(2, null)
        .containsEntry(3, 3);
  }

  @Test
  public void testToMapWithNullValuesDuplicate() throws Exception {
    assertThatThrownBy(() -> Stream.of(1, 2, 3, 1)
        .collect(LambdaUtilities.toMapWithNullValues(Function.identity(), x -> x % 2 == 1 ? x : null)))
            .isExactlyInstanceOf(IllegalStateException.class)
            .hasMessage("Duplicate key 1");
  }

  @Test
  public void testToMapWithNullValuesParallel() throws Exception {
    Map<Integer, Integer> result = Stream.of(1, 2, 3)
        .parallel() // this causes .combiner() to be called
        .collect(LambdaUtilities.toMapWithNullValues(Function.identity(), x -> x % 2 == 1 ? x : null));

    assertThat(result)
        .isExactlyInstanceOf(HashMap.class)
        .hasSize(3)
        .containsEntry(1, 1)
        .containsEntry(2, null)
        .containsEntry(3, 3);
  }

  @Test
  public void testToMapWithNullValuesParallelWithDuplicates() throws Exception {
    assertThatThrownBy(() -> Stream.of(1, 2, 3, 1, 2, 3)
        .parallel() // this causes .combiner() to be called
        .collect(LambdaUtilities.toMapWithNullValues(Function.identity(), x -> x % 2 == 1 ? x : null)))
            .isExactlyInstanceOf(IllegalStateException.class)
            .hasCauseExactlyInstanceOf(IllegalStateException.class)
            .hasStackTraceContaining("Duplicate key");
  }

এবং আপনি এটি কীভাবে ব্যবহার করবেন? ঠিক আছে, toMap()পরীক্ষাগুলির শোয়ের পরিবর্তে এটি ব্যবহার করুন । এটি কলিং কোডটি যতটা সম্ভব পরিষ্কার দেখতে দেয়।

সম্পাদনা:
নীচে হলার ধারণাটি বাস্তবায়িত করেছেন, একটি পরীক্ষা পদ্ধতি যুক্ত করেছেন


1
সংযুক্তকারী সদৃশ কীগুলির জন্য পরীক্ষা করে না। আপনি যদি প্রতিটি কী পরীক্ষা করতে এড়াতে চান তবে আপনি এমন কিছু ব্যবহার করতে পারেন(map1, map2) -> { int total = map1.size() + map2.size(); map1.putAll(map2); if(map1.size() < total.size()) throw new IllegalStateException("Duplicate key(s)"); return map1; }
হোলার

@ হোলজার ইয়েপ, এটি সত্য। বিশেষত যেহেতু accumulator()আসলে এটি পরীক্ষা করে না। হয়তো আমার একবারে কিছু সমান্তরাল স্ট্রিম করা উচিত :)
sjngm

7

@ এমমানুয়েলটজারি প্রস্তাবিত তুলনায় এখানে কিছুটা সহজ সংগ্রাহক। পছন্দ হলে এটি ব্যবহার করুন:

public static <T, K, U> Collector<T, ?, Map<K, U>> toMapNullFriendly(
        Function<? super T, ? extends K> keyMapper,
        Function<? super T, ? extends U> valueMapper) {
    @SuppressWarnings("unchecked")
    U none = (U) new Object();
    return Collectors.collectingAndThen(
            Collectors.<T, K, U> toMap(keyMapper,
                    valueMapper.andThen(v -> v == null ? none : v)), map -> {
                map.replaceAll((k, v) -> v == none ? null : v);
                return map;
            });
}

আমরা কেবল nullকিছু কাস্টম অবজেক্টের সাথে প্রতিস্থাপন noneকরি এবং ফিনিশারে বিপরীত অপারেশন করি।


5

মানটি যদি স্ট্রিং হয় তবে এটি কাজ করতে পারে: map.entrySet().stream().collect(Collectors.toMap(e -> e.getKey(), e -> Optional.ofNullable(e.getValue()).orElse("")))


4
এটি কেবলমাত্র যদি আপনি ডেটা সংশোধন করতে ঠিক থাকেন তবে কাজ করে। ডাউন স্ট্রিম পদ্ধতিগুলি খালি স্ট্রিংয়ের পরিবর্তে নাল মান আশা করতে পারে।
স্যাম বুচমিলার

3

অনুযায়ী Stacktrace

Exception in thread "main" java.lang.NullPointerException
at java.util.HashMap.merge(HashMap.java:1216)
at java.util.stream.Collectors.lambda$toMap$148(Collectors.java:1320)
at java.util.stream.Collectors$$Lambda$5/391359742.accept(Unknown Source)
at java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1359)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:512)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:502)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
at com.guice.Main.main(Main.java:28)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)

যখন বলা হয় map.merge

        BiConsumer<M, T> accumulator
            = (map, element) -> map.merge(keyMapper.apply(element),
                                          valueMapper.apply(element), mergeFunction);

এটি nullপ্রথম জিনিস হিসাবে একটি চেক করবে

if (value == null)
    throw new NullPointerException();

আমি ঘন ঘন জাভা 8 ব্যবহার করি না তাই এটি ঠিক করার আরও ভাল উপায় আছে কিনা তা আমি জানি না তবে এটি ঠিক করা কিছুটা শক্ত।

আপনি করতে পারেন:

সমস্ত নুল মানগুলিকে ফিল্টার করতে ফিল্টার ব্যবহার করুন এবং জাভাস্ক্রিপ্ট কোড চেক করে সার্ভার এই আইডির জন্য কোনও উত্তর না পাঠিয়েছে এর অর্থ হল যে সে এর জবাব দেয়নি।

এটার মতো কিছু:

Map<Integer, Boolean> answerMap =
        answerList
                .stream()
                .filter((a) -> a.getAnswer() != null)
                .collect(Collectors.toMap(Answer::getId, Answer::getAnswer));

বা উঁকি ব্যবহার করুন, যা উপাদানটির জন্য স্ট্রিম উপাদানকে পরিবর্তন করতে ব্যবহৃত হয়। উঁকি দিয়ে আপনি মানচিত্রের জন্য আরও গ্রহণযোগ্য কিছুতে উত্তর পরিবর্তন করতে পারেন তবে এর অর্থ আপনার যুক্তিটি কিছুটা সম্পাদনা করা।

আপনি যদি বর্তমান ডিজাইনটি এড়াতে চান তবে মনে হয় Collectors.toMap


3

আমি ইমানুয়েল টুজারির বাস্তবায়নে কিছুটা পরিবর্তন করেছি ।

এই সংস্করণ;

  • নাল কীগুলি অনুমতি দেয়
  • নাল মানকে অনুমতি দেয়
  • ডুপ্লিকেট কীগুলি সনাক্ত করে (সেগুলি নাল থাকলেও) এবং আসল জেডিকে বাস্তবায়নের মতো ইলিজেলস্টেট এক্সসেপশনটি নিক্ষেপ করে।
  • কীটি ইতিমধ্যে নাল মানটিতে ম্যাপ করলেও নকল কীগুলি সনাক্ত করে। অন্য কথায়, নো-ম্যাপিং থেকে নাল-মান সহ একটি ম্যাপিং পৃথক করে।
public static <T, K, U> Collector<T, ?, Map<K, U>> toMapOfNullables(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper) {
    return Collectors.collectingAndThen(
        Collectors.toList(),
        list -> {
            Map<K, U> map = new LinkedHashMap<>();
            list.forEach(item -> {
                K key = keyMapper.apply(item);
                if (map.containsKey(key)) {
                    throw new IllegalStateException(String.format("Duplicate key %s", key));
                }
                map.put(key, valueMapper.apply(item));
            });
            return map;
        }
    );
}

ইউনিট পরীক্ষা:

@Test
public void toMapOfNullables_WhenHasNullKey() {
    assertEquals(singletonMap(null, "value"),
        Stream.of("ignored").collect(Utils.toMapOfNullables(i -> null, i -> "value"))
    );
}

@Test
public void toMapOfNullables_WhenHasNullValue() {
    assertEquals(singletonMap("key", null),
        Stream.of("ignored").collect(Utils.toMapOfNullables(i -> "key", i -> null))
    );
}

@Test
public void toMapOfNullables_WhenHasDuplicateNullKeys() {
    assertThrows(new IllegalStateException("Duplicate key null"),
        () -> Stream.of(1, 2, 3).collect(Utils.toMapOfNullables(i -> null, i -> i))
    );
}

@Test
public void toMapOfNullables_WhenHasDuplicateKeys_NoneHasNullValue() {
    assertThrows(new IllegalStateException("Duplicate key duplicated-key"),
        () -> Stream.of(1, 2, 3).collect(Utils.toMapOfNullables(i -> "duplicated-key", i -> i))
    );
}

@Test
public void toMapOfNullables_WhenHasDuplicateKeys_OneHasNullValue() {
    assertThrows(new IllegalStateException("Duplicate key duplicated-key"),
        () -> Stream.of(1, null, 3).collect(Utils.toMapOfNullables(i -> "duplicated-key", i -> i))
    );
}

@Test
public void toMapOfNullables_WhenHasDuplicateKeys_AllHasNullValue() {
    assertThrows(new IllegalStateException("Duplicate key duplicated-key"),
        () -> Stream.of(null, null, null).collect(Utils.toMapOfNullables(i -> "duplicated-key", i -> i))
    );
}

1

পুরানো প্রশ্নটি আবার খোলার জন্য দুঃখিত, তবে যেহেতু সম্প্রতি এটি সম্পাদনা করা হয়েছিল যে জাভা ১১-এ এখনও "ইস্যু" রয়ে গেছে তাই আমার মনে হয়েছিল আমি এটিকে নির্দেশ করতে চাই:

answerList
        .stream()
        .collect(Collectors.toMap(Answer::getId, Answer::getAnswer));

আপনাকে নাল পয়েন্টার ব্যতিক্রম দেয় কারণ মানচিত্রটি মান হিসাবে শূন্য করতে দেয় না। এটি উপলব্ধি করে কারণ আপনি যদি কীটির জন্য কোনও মানচিত্রে সন্ধান করেন kএবং এটি উপস্থিত না থেকে থাকে তবে প্রত্যাবর্তিত মানটি ইতিমধ্যে null(জাভাদোক দেখুন)। সুতরাং আপনি যদি kমানটি রাখতে সক্ষম হন nullতবে মানচিত্রটি দেখতে দেখতে অদ্ভুত আচরণ করছে look

যেমন কেউ মন্তব্যগুলিতে বলেছে, ফিল্টারিং ব্যবহার করে এটি সমাধান করা বেশ সহজ:

answerList
        .stream()
        .filter(a -> a.getAnswer() != null)
        .collect(Collectors.toMap(Answer::getId, Answer::getAnswer));

এইভাবে nullমানচিত্রে কোনও মান সন্নিবেশ করা হবে না এবং মানচিত্রে nullকোনও উত্তর নেই এমন একটি আইডি খুঁজলে আপনি "মান" হিসাবে পাবেন ।

আমি আশা করি এটি সবার কাছে উপলব্ধিযোগ্য।


1
কোনও মানচিত্র নাল মানগুলিতে অনুমতি না দিলে তা বোঝা যাবে, তবে তা করে। আপনি answerMap.put(4, null);কোনও সমস্যা ছাড়াই করতে পারেন । আপনি ঠিক বলেছেন যে আপনার প্রস্তাবিত সমাধানের সাহায্যে আপনি anserMap.get () এর জন্য একই ফলাফল পাবেন যদি মানটি নাল হিসাবে সন্নিবেশ করানো হয় তবে এটি উপস্থিত না থাকলে। তবে, আপনি যদি মানচিত্রের সমস্ত এন্ট্রিগুলি নিয়ে পুনরাবৃত্তি করেন তবে স্পষ্টতই একটি পার্থক্য রয়েছে।
জ্যাস্পার

এটি আমার মামলার সহজ সমাধান, আপনাকে ধন্যবাদ
আহমকারা

1
public static <T, K, V> Collector<T, HashMap<K, V>, HashMap<K, V>> toHashMap(
        Function<? super T, ? extends K> keyMapper,
        Function<? super T, ? extends V> valueMapper
)
{
    return Collector.of(
            HashMap::new,
            (map, t) -> map.put(keyMapper.apply(t), valueMapper.apply(t)),
            (map1, map2) -> {
                map1.putAll(map2);
                return map1;
            }
    );
}

public static <T, K> Collector<T, HashMap<K, T>, HashMap<K, T>> toHashMap(
        Function<? super T, ? extends K> keyMapper
)
{
    return toHashMap(keyMapper, Function.identity());
}

1
upvoting কারণ এই সংকলন। গৃহীত উত্তর সংকলন করে না কারণ মানচিত্র :: পুটএলে সমস্তটির ফেরতের মান নেই।
টগজিনিটস

0

সমস্ত ছোট আইফোন দিয়ে প্রশ্নগুলি পুনরুদ্ধার করা

Map<Integer, Boolean> answerMap = 
  answerList.stream()
            .collect(Collectors.toMap(Answer::getId, a -> 
                       Boolean.TRUE.equals(a.getAnswer())));

আমি মনে করি এটি সেরা উত্তর - এটি সর্বাধিক সংক্ষিপ্ত উত্তর এবং এটি এনপিই সমস্যা সমাধান করে।
এলকনরাড

-3

নুলপয়েন্টার এক্সসেপশনটি এখন পর্যন্ত সবচেয়ে বেশি ঘন ঘন আসার ব্যতিক্রম (অন্তত আমার ক্ষেত্রে)। এটি এড়াতে আমি প্রতিরক্ষামূলক হয়ে গিয়েছি এবং নাল চেকগুলি সংযোজন করি এবং আমি ফুলে ও কুৎসিত কোডটি শেষ করি। জাভা 8 নাল রেফারেন্সগুলি পরিচালনা করতে introduচ্ছিক পরিচয় করিয়ে দেয় যাতে আপনি অযোগ্য এবং অ-মূল্যহীন মানগুলি সংজ্ঞায়িত করতে পারেন।

এটি বলেছিল, আমি allচ্ছিক পাত্রে সমস্ত অযোগ্য রেফারেন্সগুলিকে মোড়া করব। আমাদের পাশাপাশি পিছিয়ে পড়া সামঞ্জস্যতাও ভাঙা উচিত নয়। কোডটি এখানে।

class Answer {
    private int id;
    private Optional<Boolean> answer;

    Answer() {
    }

    Answer(int id, Boolean answer) {
        this.id = id;
        this.answer = Optional.ofNullable(answer);
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    /**
     * Gets the answer which can be a null value. Use {@link #getAnswerAsOptional()} instead.
     *
     * @return the answer which can be a null value
     */
    public Boolean getAnswer() {
        // What should be the default value? If we return null the callers will be at higher risk of having NPE
        return answer.orElse(null);
    }

    /**
     * Gets the optional answer.
     *
     * @return the answer which is contained in {@code Optional}.
     */
    public Optional<Boolean> getAnswerAsOptional() {
        return answer;
    }

    /**
     * Gets the answer or the supplied default value.
     *
     * @return the answer or the supplied default value.
     */
    public boolean getAnswerOrDefault(boolean defaultValue) {
        return answer.orElse(defaultValue);
    }

    public void setAnswer(Boolean answer) {
        this.answer = Optional.ofNullable(answer);
    }
}

public class Main {
    public static void main(String[] args) {
        List<Answer> answerList = new ArrayList<>();

        answerList.add(new Answer(1, true));
        answerList.add(new Answer(2, true));
        answerList.add(new Answer(3, null));

        // map with optional answers (i.e. with null)
        Map<Integer, Optional<Boolean>> answerMapWithOptionals = answerList.stream()
                .collect(Collectors.toMap(Answer::getId, Answer::getAnswerAsOptional));

        // map in which null values are removed
        Map<Integer, Boolean> answerMapWithoutNulls = answerList.stream()
                .filter(a -> a.getAnswerAsOptional().isPresent())
                .collect(Collectors.toMap(Answer::getId, Answer::getAnswer));

        // map in which null values are treated as false by default
        Map<Integer, Boolean> answerMapWithDefaults = answerList.stream()
                .collect(Collectors.toMap(a -> a.getId(), a -> a.getAnswerOrDefault(false)));

        System.out.println("With Optional: " + answerMapWithOptionals);
        System.out.println("Without Nulls: " + answerMapWithoutNulls);
        System.out.println("Wit Defaults: " + answerMapWithDefaults);
    }
}

1
অকেজো উত্তর, কেন এটি সমাধান করতে আপনার নাল থেকে মুক্তি পাওয়া উচিত? এটি Collectors.toMap()নাল মান না হওয়ার সমস্যা
এনারসিসিও

@ আন্তর্সিও বন্ধুকে শান্ত করুন !! নাল মানগুলির উপর নির্ভর করা ভাল অভ্যাস নয়। আপনি যদি ptionচ্ছিক ব্যবহার করে থাকেন তবে আপনি এনপিইর প্রথম স্থানে মুখোমুখি হতেন না। Ptionচ্ছিক ব্যবহারগুলি পড়ুন।
ট্রাইকোর

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