使用流按 List<String> 排序 Map<String, Integer>

Ordering Map<String, Integer> by List<String> using streams

Map<String, Integer> nonOrderedData = // {b=1, c=2, d=3, e=4, a=0}
List<String> orderSequence = // a, b, c, d, e

我需要应用排序序列来获取正确排序的数据,我如何使用(首选)流实现此目的?

我用的是非流方式:

Map<String, Integer> orderedData = new HashMap<>();
for (Map.Entry<String, Integer> nod : nonOrderedData.entrySet()) {
    for (String os : orderSequence) {
        if (os == nod.getKey()) {
            // add nonOrderedData data
        } else {
            // add data by sequence
        }
    }
}

想要用更简洁的方式来实现我想要的东西。


我注意到在我的方法中我可以只 return new TreeMap<>(nonOrderedData) 它会工作得很好,但我不想坚持只应用 asc 顺序 - 我想读取实际序列值,然后更改 nonOrderedData.

HashMap不能用来存储orderedData,因为它不保证其键的顺序,所以应该使用LinkedHashMap来维护插入顺序。

使用流按 orderSequence 中显示的数据排序可以通过两种模式实现:

  1. 仅保留 nonOrderedData:
  2. 中可用的值
Map<String, Integer> nonOrderedData = Map.of(
    "b", 1, "e", 4, "a", 0, "o", 5, "d", 7
);
List<String> orderSequence = Arrays.asList(
    "a", "e", "i", "o", "u", "b", "c", "d"
);

Map<String, Integer> reordered = orderSequence
    .stream()
    .filter(nonOrderedData::containsKey)
    .collect(Collectors.toMap(
        key -> key, nonOrderedData::get, 
        (v1, v2) -> v1, LinkedHashMap::new
    ));
    
System.out.println(reordered);

输出:

{a=0, e=4, o=5, b=1, d=7}

orderSequencenonOrderedData之间类似于INNER JOIN

  1. 如果 orderSequence 中的密钥在 nonOrderedData 中丢失,则用一些默认值填充 reorderedData:
Map<String, Integer> reorderedWithDefault = orderSequence
    .stream()
    .collect(Collectors.toMap(
        key -> key, nonOrderedData.getOrDefault(key, -1), 
        (v1, v2) -> v1, LinkedHashMap::new
    ));
    
System.out.println(reorderedWithDefault);

输出:

{a=0, e=4, i=-1, o=5, u=-1, b=1, c=-1, d=7}

orderSequencenonOrderedData之间类似于LEFT JOIN


更新
在上述实现中,nonOrderedData 中与 orderSequence 中的键不匹配的键值对被完全跳过。可以使用 Map::remove (Object key) 跟踪此类键(并稍后添加到 reordered 结果),其中 returns 键的值被删除。

然而,下面的两个代码示例在流执行之外修改了nonOrderedData的状态。

  1. 只保留来自nonOrderedData的键和相关值,将不匹配的对放在最后:
Map<String, Integer> nonOrderedData = new HashMap<>(Map.of(
    "b", 1, "e", 4, "z", 8, "a", 0, "q", 6,
    "f", 5, "d", 7
    ));
List<String> orderSequence = Arrays.asList("a", "e", "i", "o", "u", "b", "c", "d");

Map<String, Integer> reordered = orderSequence
    .stream()
    .filter(nonOrderedData::containsKey)
    .collect(Collectors.toMap(
        key -> key, nonOrderedData::remove, 
        (v1, v2) -> v1, LinkedHashMap::new
    ));

SortedMap<String, Integer> remainder = new TreeMap<>(nonOrderedData);
System.out.println("remained: " + remainder);
    
reordered.putAll(remainder);

System.out.println(reordered);

输出:

remained: {f=5, q=6, z=8}
{a=0, e=4, b=1, d=7, f=5, q=6, z=8}

orderSequencenonOrderedData之间类似于RIGHT JOIN

  1. 保持 orderSequencenonOrderedData 的所有值类似于 FULL JOIN

此处将为 orderSequence 中的非映射键提供默认值,并将 nonOrderedData 中的非匹配键添加到末尾。

Map<String, Integer> reorderedFull = orderSequence
    .stream()
    .peek(key -> nonOrderedData.computeIfAbsent(key, (k) -> -1)) // default value
    .collect(Collectors.toMap(
        key -> key, nonOrderedData::remove, 
        (v1, v2) -> v1, LinkedHashMap::new
    ));

SortedMap<String, Integer> remainderFull = new TreeMap<>(nonOrderedData);
System.out.println("remained: " + remainderFull);
    
reorderedFull.putAll(remainderFull);
System.out.println(reorderedFull);

输出:

remained: {f=5, q=6, z=8}
{a=0, e=4, i=-1, o=-1, u=-1, b=1, c=-1, d=7, f=5, q=6, z=8}

如果您有一个有序序列,您可以从该序列收集一个包含值的新映射 无序映射 使用 remove 方法, 从该映射中删除键 的映射和 returns 前一个值.

收集新地图后,如果无序地图中还有剩余内容,您可以使用putAll方法添加这些条目。

Map<String, Integer> nonOrderedData = new HashMap<>(
        Map.of("f", 5, "b", 1, "c", 2, "d", 3, "e", 4, "a", 0));

List<String> orderSequence = List.of("a", "b", "c", "d", "e");
Map<String, Integer> orderedMap = orderSequence.stream()
        .collect(LinkedHashMap::new,
                (col, e) -> col.put(e, nonOrderedData.remove(e)),
                HashMap::putAll);
System.out.println(orderedMap); // {a=0, b=1, c=2, d=3, e=4}
System.out.println(nonOrderedData); // {f=5}

orderedMap.putAll(nonOrderedData); // add what's left

System.out.println(orderedMap); // {a=0, b=1, c=2, d=3, e=4, f=5}

同样,您可以收集地图条目列表:

,而不是地图
Map<String, Integer> nonOrderedData = new HashMap<>(
        Map.of("f", 5, "b", 1, "c", 2, "d", 3, "e", 4, "a", 0));

List<String> orderSequence = List.of("a", "b", "c", "d", "e");
List<Map.Entry<String, Integer>> orderedList = orderSequence.stream()
        .map(e -> Map.entry(e, nonOrderedData.remove(e)))
        .collect(Collectors.toList());
System.out.println(orderedList); // [a=0, b=1, c=2, d=3, e=4]
System.out.println(nonOrderedData); // {f=5}

orderedList.addAll(nonOrderedData.entrySet()); // add what's left

System.out.println(orderedList); // [a=0, b=1, c=2, d=3, e=4, f=5]