簡體   English   中英

將與 Lowest 關聯的 HashMap 中的所有 Key 收集到 List 中

[英]Collect all the Keys from a HashMap associated with the Lowest into a List

如何按值對 HashMap 條目進行排序並打印映射最低值的所有鍵

這是我的HashMap

HashMap<String, Integer> map = new HashMap<>();
map.put("John", 1);
map.put("Matthew", 12);
map.put("Clara", 53);
map.put("Keith", 2);

預期輸出:

John

我正在嘗試對它們進行排序,以便將“John”收集到列表中。

List<String> keys = res.entrySet().stream()
    .sorted(Map.Entry.<String, Integer>comparingByValue())
    .limit(1)
    .map(Map.Entry::getKey)
    .collect(Collectors.toList());

現在,讓我們考慮另一張地圖。

HashMap<String, Integer> map = new HashMap<>();
map.put("John", 2);
map.put("Matthew", 12);
map.put("Clara", 53);
map.put("Keith", 2);

預期輸出:

John, Keith

如果出現平局(即兩者都與最低值相關聯),我如何才能將JohnKeith添加到列表中?

將條目收集到按其值分組的Map<Integer, List<Map.Entry>>中,獲取對應於最小值的條目,然后將這些條目轉換為它們的鍵列表:

List<String> lowestValuedNames = map.entrySet().stream()
  .collect(Collectors.groupingBy(Map.Entry::getValue))
  .get(Collections.min(map.values()))
  .stream()
  .map(Map.Entry::getKey)
  .collect(Collectors.toList());

現場演示

不是特別有效,但使用一條線完成。

它可以在不排序的情況下完成,也無需創建中間映射(並對它的條目執行額外的迭代以找到具有最低鍵的條目)。 即我們可以在內存分配和性能方面做得更好。

為此,我們需要維護一個迄今為止遇到的最低值的條目集合,並將每個遇到的條目的值與集合中第一個元素的值進行比較。 如果集合為空或值相同,我們需要添加下一個條目。 如果下一個條目的值較低,我們需要清除集合,然后添加條目。 並且下一個條目的值大於我們應該忽略它。

要使用流實現這種方法,我們可以使用靜態方法Collector.of()創建自定義收集器。

這就是它的實現方式:

public static void main(String[] args) {
    Map<String, Integer> people = 
        Map.of("John", 2, "Matthew", 12,
               "Clara", 53, "Keith", 2);

    List<String> result = people.entrySet().stream()
        .collect(Collector.of(
            ArrayDeque::new,
            (Deque<Map.Entry<String, Integer>> deque, Map.Entry<String, Integer> next) -> {
                if (!deque.isEmpty() && deque.peek().getValue() > next.getValue()) deque.clear();
                if (deque.isEmpty() || deque.peek().getValue().equals(next.getValue())) deque.add(next);
            },
            (left, right) -> {
                if (left.isEmpty()) return right;
                if (right.isEmpty()) return left;
                if (left.peek().getValue() < right.peek().getValue()) return left;
                if (left.peek().getValue() > right.peek().getValue()) return right;
                left.addAll(right);
                return left;
            },
            deque -> deque.stream().map(Map.Entry::getKey).collect(Collectors.toList())
        ));

    System.out.println(result);
}

收集器仍在對生成的條目集合執行最終迭代以提取 我們可以省略這一步並通過將當前駐留在收集器中的邏輯封裝到單獨的類中來簡化代碼。 並且該類的實例將被收集器用作可變容器而不是集合,並將負責進行所有內務處理。

class EntryContainer implements Consumer<Map.Entry<String, Integer>> {
    private int value;
    private List<String> names = new ArrayList<>();
    
    @Override
    public void accept(Map.Entry<String, Integer> next) {
        if (!names.isEmpty() && next.getValue() < value) names.clear();
        if (names.isEmpty() || value == next.getValue()) {
            names.add(next.getKey());
            value = next.getValue();
        }
    }
    
    public EntryContainer merge(EntryContainer other) {
        if (names.isEmpty() || !other.names.isEmpty() && other.value < value) return other;
        if (other.names.isEmpty() || value < other.value) return this;
        names.addAll(other.names);
        return this;
    }
    
    public List<String> getNames() {
        return names;
    }
}

現在我們可以應用它了。 收集器看起來不再令人生畏,我們也不需要終結器函數,而是在流執行結束時由收集器的容器分發名稱集合:

public static void main(String[] args) {
    Map<String, Integer> people =
        Map.of("John", 2, "Matthew", 12,
               "Clara", 53, "Keith", 2);
    
    List<String> result = people.entrySet().stream()
        .collect(Collector.of(
            EntryContainer::new,
            EntryContainer::accept,
            EntryContainer::merge
        ))
        .getNames();
    
    System.out.println(result);
}

輸出:

[Keith, John]

暫無
暫無

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

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