如何比较两个MultiMap?

7

我有两个Multimap,它们是从两个巨大的CSV文件创建的。

Multimap<String, SomeClassObject> mapOne = ArrayListMultimap.create();
Multimap<String, SomeClassObject> mapTwo = ArrayListMultimap.create();

我假定一个CSV列作为键,并且每个键都有数千个相应的值。这些Multimap中包含的数据应该是相同的。现在我想比较这些Multimap中的数据,并查找是否有不同的值。这里有两种方法: 方法一:Multimap中制作一个大列表。这个大列表将包含几个独立的列表。每个较小的列表包含从Multimap中读取的唯一值,即“键”,以及形成该个体列表其余部分的关联值。
ArrayList<Collection<SomeClassObject>> bigList = new ArrayList<Collection<SomeClassObject>>();

bigList中将包含各个小列表A、B、C等。
我计划从每个文件的bigList中挑选单独的列表,基于检查来自第二个Multimap的个别列表是否包含该“key”元素。如果是,则比较这两个列表并找到任何无法匹配的内容。
方法二:
比较两个Multimap,但我不确定如何完成。
哪种方法的执行时间更短?我需要在最短的时间内完成操作。

你想知道它们是否相等,还是想获取缺失值列表? - durron597
我有两个Multimap,它们是从两个巨大的CSV文件创建的。为什么你要在内存中处理呢?为什么不使用数据库呢? - Amir Afghani
@durron597 首先检查第一个multimap中正在检查的条目的键是否在第二个multimap中可用。如果是,则检查两个multimap中与该键相关联的值在每个方面上是否相等。如果它们在任何方面上不同,则将认为该记录不同,并需要将其取出并相应地处理。 - user3044240
2个回答

7

使用Multimaps.filterEntries(Multimap, Predicate)方法。

如果您想获取两个Multimap之间的差异,可以很容易地基于containsEntry编写一个过滤器,然后使用过滤行为高效地查找所有不匹配的元素。只需基于一个映射构建Predicate,然后过滤另一个映射即可。

这是我的意思。这里,我使用Java 8 lambdas,但您可以查看此帖子的修订历史记录以查看Java 7版本:

public static void main(String[] args) {
  Multimap<String, String> first = ArrayListMultimap.create();
  Multimap<String, String> second = ArrayListMultimap.create();
  
  first.put("foo", "foo");
  first.put("foo", "bar");
  first.put("foo", "baz");
  first.put("bar", "foo");
  first.put("baz", "bar");
  
  second.put("foo", "foo");
  second.put("foo", "bar");
  second.put("baz", "baz");
  second.put("bar", "foo");
  second.put("baz", "bar");
       
  Multimap<String, String> firstSecondDifference =
      Multimaps.filterEntries(first, e -> !second.containsEntry(e.getKey(), e.getValue()));
  
  Multimap<String, String> secondFirstDifference =
      Multimaps.filterEntries(second, e -> !first.containsEntry(e.getKey(), e.getValue()));
  
  System.out.println(firstSecondDifference);
  System.out.println(secondFirstDifference);
}

在这个人为构造的例子中,Output是不在另一个列表中的元素:

{foo=[baz]}
{baz=[baz]}

如果多个地图匹配,这些multimap将为空。


在Java 7中,您可以手动创建谓词,例如:

public static class FilterPredicate<K, V> implements Predicate<Map.Entry<K, V>> {
  private final Multimap<K, V> filterAgainst;

  public FilterPredicate(Multimap<K, V> filterAgainst) {
    this.filterAgainst = filterAgainst;
  }

  @Override
  public boolean apply(Entry<K, V> arg0) {
    return !filterAgainst.containsEntry(arg0.getKey(), arg0.getValue());
  }
}

将其作为参数传递给Multimaps.filterEntries(),例如:

Multimap<String, String> firstSecondDifference =
    Multimaps.filterEntries(first, new FilterPredicate(second));

Multimap<String, String> secondFirstDifference =
    Multimaps.filterEntries(second, new FilterPredicate(first));

否则,代码与上面的Java 8版本相同(结果也相同)。

Java 7 版本。我认为你应该把你删除的代码放回去,那样更容易理解。谢谢。 - user3044240
@user3044240,它已经被编辑回来了,以一种解释两个版本的方式。Java 8版本的代码明显更少,我会保留两个版本,因为我们已经过了Java 7的官方生命周期结束。 - durron597
可能听起来有点烦人,但我刚刚改了代码,使用了lambda版本,但两种方式都很好用。再次感谢! - user3044240
2
@user3044240 或许将来会有用户查看这个答案并需要 Java 7 版本,我会保留原样。 - durron597
我仍然被困在Java 7中,所以这非常有用。 - CheeseFerret
显示剩余2条评论

2
根据ArrayListMultimap.equals文档:

将指定的对象与此多重映射进行比较以确定相等性。

如果两个ListMultimap实例包含每个键相同顺序的值,则它们是相等的。如果值的顺序不同,则这些多重映射将不被视为相等。

因此只需执行mapOne.equals(mapTwo)。通过自己实现,您不会获得更好的执行时间。

我该如何确保multimap每个key的值的顺序相同? - user3044240
1
@durron597 我不知道,但是Jean的回答表明为了使用他的equals方法,需要排序。我的担忧是检查在一个映射中检查键的任何值,这些值也应该在另一个映射的相同键的值中。如果找不到它们,那么这意味着两个文件中的记录不同,并且需要处理。 - user3044240
1
@user3044240 它确实会检查顺序,就像文档中所述的那样。和 arrayList.equals(otherList) 的方式一样。 - Jean Logeart
@user3044240 再次阅读文档:“如果对于每个键,两个ListMultimap实例包含相同顺序的相同值,则它们是相等的。” - Louis Wasserman
@user3044240 为什么不使用 SetMultimap 呢? "如果对于每个键,两个 SetMultimap 实例包含相同的值,则它们是相等的。相等性不取决于键或值的排序。" - Omar Hrynkiewicz
据我所知,在他的情况下,顺序很重要。 - Jean Logeart

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接