簡體   English   中英

使用 Java 8 流來轉換帶有空值的 Map

[英]Use Java 8 streams to transform a Map with nulls

我正在處理一個Map<String,String> ,它在鍵和/或值中有null個條目:

Map<String, String> headers = new HashMap<>();
headers.put("SomE", "GreETing");
headers.put("HELLO", null);
headers.put(null, "WOrLd");

headers.keySet().stream().forEach(k -> System.out.println(k + " => " + copy.get(k)));

我得到以下 output:

SomE => GreETing
HELLO => null
null => WOrLd

我需要轉換 map,所以所有非空值都轉換為小寫,如下所示:

some => greeting
hello => null
null => world

我正在嘗試使用 Java 8 流 API,但以下代碼拋出NullPointerException

Map<String,String> copy
    = headers.entrySet()
             .stream()
             .collect(
                 Collectors.toMap(
                     it -> it.getKey() != null ? it.getKey().toLowerCase() : null,
                     it -> it.getValue() != null ? it.getValue().toLowerCase() : null));

copy.keySet().stream().forEach(k -> System.out.println(k + " => " + copy.get(k)));

如果我注釋掉最后兩個 map 條目,程序就會執行,因此當鍵或值為 null 時, Collectors.toMap的工作方式一定存在問題。如何使用流 API 來解決這個問題?

問題是toMap()調用正在構建的底層Map實現的merge()函數,該函數不允許值為null

來自javadoc for Map#merge (強調我的)

如果指定的鍵尚未與值關聯或與null關聯,則將其與給定的非空值關聯。 否則,將相關值替換為給定重映射函數的結果,或者如果結果為null則刪除。

因此使用Collectors.toMap()將無法正常工作。

您可以在沒有流的情況下做到這一點:

Map<String,String> copy = new HashMap<>();

for(Entry<String, String> entry : headers.entrySet()){
    copy.put(entry.getKey() !=null ? entry.getKey().toLowerCase() : null, 
             entry.getValue() !=null ? entry.getValue().toLowerCase() : null
            );
}

使用收集:

final Function<String, String> fn= str -> str == null ? null : str.toLowerCase();
Map<String, String> copy = headers.entrySet().stream()
   .collect(HashMap::new,
            (m, e) -> m.put(fn.apply(e.getKey()), fn.apply(e.getValue())), 
            Map::putAll);

或者使用AbacusUtil

Map<String, String> copy = Stream.of(headers)
   .collect(HashMap::new, 
     (m, e) -> m.put(N.toLowerCase(e.getKey()), N.toLowerCase(e.getValue())));

2月4日更新,或者:

Map<String, String> copy = EntryStream.of(headers)
   .toMap(entry -> N.toLowerCase(entry.getKey()), entry -> N.toLowerCase(entry.getValue()));

你不能在沒有獲得NPE的情況下使用Collectors.toMap() ,因為你的map有一個null value ,正如@dkatzel已經解釋的那樣,但我仍然想要使用Stream API ;

Map<String, String> headers = new HashMap<>();
headers.put("good", "AsDf");
headers.put("SomE", "GreETing");
headers.put("HELLO", null);
headers.put(null, "WOrLd");

new HashSet<>(headers.entrySet()).stream()
    .peek(entry -> entry.setValue(Objects.isNull(entry.getValue()) ? null : entry.getValue().toLowerCase()))
    .filter(entry -> !Objects.isNull(entry.getKey()) && !entry.getKey().equals(entry.getKey().toLowerCase()))
    .forEach(entry -> {
        headers.put(entry.getKey().toLowerCase(), entry.getValue());
        headers.remove(entry.getKey());
    });

System.out.println(headers);

打印出來;

{null=world, some=greeting, hello=null, good=asdf}

如果您有復雜的檢查並且仍然想使用不帶空值的 Collectors.toMap()

// convert data to a final key, value pairs using Pair.of(key, value)
.map(data -> {
    SomeKey key = makeKey(data.getKey());
    SomeVal val = makeValue(data.getValue());
    
    // validate and return 
    if(key.foo != null && val.bar != null) {
        return Pair.of(key.foo, val.bar);
    }
    return null;
})
.filter(Objects::nonNull)   // remove the ones that don't satisy criteria
.collect(Collectors.toMap(pair -> pair.getLeft(), pair -> pair.getRight(),  // then collect final key/value data without NPE checks
    (left, right) -> {
        left.merge(right); // can return null
        return left;
    }
)));

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM