比较两个 Maps<String, List<String>> 得到 map key + map value 作为差异

Compare two Maps<String, List<String>> and get map key + map value as differences

我有两个 Map 类型的映射,我需要比较它们,当我在列表中得到不同的值时,我需要用它们对应的映射检索这些值 key.The 两个映射看起来像这样:

    Map<String, List<String>> map1 = new TreeMap<>();
    Map<String, List<String>> map2 = new TreeMap<>();

    map1.put("Column1", Arrays.asList("one", "two", "four"));
    map1.put("Column2", Arrays.asList("one", "two", "four"));

    map2.put("Column1", Arrays.asList("one", "two", "three"));
    map2.put("Column2", Arrays.asList("one", "two", "three"));

比较后,结果应该是这样的:“键 1 的值不同,元素 3。键 2 的值不同,元素 3”。我如何遍历两个给定的映射,以便获得不同元素的相应索引?我尝试从每个 Map 创建一个 flatMap,但随后我丢失了列表之间不同元素的 keySet。

有很多方法可以进行比较(例如顺序很重要?如果不是,请使用 Set 而不是 List)。

比较“不同元素对应索引”的一种方法是:

Map<String, List<String>> diffs = Streams.concat(xs.keySet().stream(), ys.keySet().stream())
        .distinct()
        .collect(toMap(k -> k, k -> {
            List<String> x = xs.getOrDefault(k, emptyList());
            List<String> y = ys.getOrDefault(k, emptyList());
            return IntStream.range(0, Math.max(x.size(), y.size()))
                    .mapToObj(i -> i < x.size() ? (i < y.size() ? (x.get(i).equals(y.get(i)) ? "=": "<>"): "X") : "Y")
                    .collect(toList());
        }));

如果索引 i 处的元素相等,则得到 =,如果不同,则得到 <>,如果该索引存在,则得到 XY只有一套。

例如:

xs.put("Column1", Arrays.asList("one", "xxx", "four"));
xs.put("Column2", Arrays.asList("one", "two"));
xs.put("Column3", Arrays.asList("cat"));

ys.put("Column1", Arrays.asList("one", "two", "four"));
ys.put("Column2", Arrays.asList("one", "two", "three"));
ys.put("Column4", Arrays.asList("apple"));

有输出

{ Column1=[=, <>, =]
, Column2=[=, =, Y]
, Column3=[X]
, Column4=[Y]
}

这是一种方法。在不知道数据的性质的情况下,至少提及边界情况很重要。

  • 首先,你的例子暗示你在做位置差异。所以 "one","two""two","one" 不同,因此被标记为 elements 0, 1 不同。
  • 地图可能有不同的键。这意味着对于任何给定的键,缺少该键的映射内容都是不同的。
  • 对应键的列表可能有不同数量的元素。前几个可能是一样的。任何元素缺失而不仅仅是不同的元素都会被标记出来。

这是数据

Map<String, List<String>> map1 = new TreeMap<>();
Map<String, List<String>> map2 = new TreeMap<>();

map1.put("Column1", Arrays.asList("one", "two", "four"));
map1.put("Column2", Arrays.asList("one", "two", "four"));
map1.put("Column3", Arrays.asList("three", "two"));
map1.put("Column4", Arrays.asList("one","two","three"));
map1.put("Column6", Arrays.asList("nine","ten"));
    
map2.put("Column1", Arrays.asList("one", "two", "three"));
map2.put("Column2", Arrays.asList("one", "two", "three","four","five"));
map2.put("Column3", Arrays.asList("three"));
map2.put("Column4", Arrays.asList("one", "two","three"));
map2.put("Column5", Arrays.asList("one", "two","three"));

现在创建一个集合,其中包含两个地图的所有键。排序不是必需的,但可以更轻松地与数据进行比较。

SortedSet<String>keySet = new TreeSet<>(map1.keySet()); 
keySet.addAll(map2.keySet());
  • 首先,通过创建一个空列表来防止空列表(对于丢失的键)。
  • 遍历列表,比较相同键的值。
  • 如果不同,调用辅助方法获取不同的索引
  • 显示信息。
for (String key : keySet) {
    List<String>list1 = Objects.requireNonNullElse(map1.get(key), List.of());
    List<String>list2 = Objects.requireNonNullElse(map2.get(key), List.of());
    if (!list1.equals(list2)) {
        int[] diffs = getIndices(list1,list2);
        System.out.printf("Maps different at key '%s', element(s) %s", key, diffs[0]);
        for (int i = 1; i < diffs.length; i++) {
            System.out.printf(", %s", diffs[i]);
        }
        System.out.println();
    }
}

以上打印

Maps different at key 'Column1', element(s) 2
Maps different at key 'Column2', element(s) 2, 3, 4
Maps different at key 'Column3', element(s) 1
Maps different at key 'Column5', element(s) 0, 1, 2
Maps different at key 'Column6', element(s) 0, 1

辅助方法。

  • 计算列表的最小和最大大小
  • 流式传输最大列表的索引。
  • 如果索引在两个列表范围内,则进行比较
  • 如果比较不同,就传索引
  • 如果索引超出范围,则通过它,因为它会标记缺少的列表项。
  • 然后return索引数组
public static <T> int[] getIndices(List<T> list1,
        List<T> list2) {
    int min = Math.min(list1.size(), list2.size());
    int max = Math.max(list1.size(), list2.size());
    int[] diffs = IntStream.range(0, max)
            .filter(i -> i >= min || !list1.get(i).equals(list2.get(i)))
            .toArray();
    return diffs;
}