Java 比较两组不同类型数据的有效方法

Java 比较两组不同类型数据的有效方法,java,algorithm,performance,guava,Java,Algorithm,Performance,Guava,首先,我需要一些非常有效的解决方案,因为我正在比较>300k元素的集合 一开始我们有两门不同的课 Class A { String keyA; String keyB; String keyC; } Class B { String keyA; String keyB; String keyC; String name; String code; toA() { return new A(keyA, keyB, keyC);

首先,我需要一些非常有效的解决方案,因为我正在比较>300k元素的集合

一开始我们有两门不同的课

Class A {
   String keyA;
   String keyB;
   String keyC;
}

Class B {
   String keyA;
   String keyB;
   String keyC;
   String name;
   String code;

   toA() {
     return new A(keyA, keyB, keyC);
   }
}
它们都包含几个字段,这些字段由key组成(在本例中,key为三列=keyA-keyB-keyC)

这个组合键使得使用嵌套循环的原始蛮力计算非常长。 所以我发现最有效的方法是使用方法toA将第二类转换为第一类 然后我可以安全地比较它们,比如使用谷歌的api使用效率集

Set<A> collectionA = <300k of elements>
Set<B> collectionB = <300k of elements>
Set<A> collectionBConvertedToA = collectionB.stream().map(item -> item.toA()).collect(toSet())

Set<A> result = Sets.differences(collectionBConvertedToA, collectionA); // very fast first full scan comparison

Set<String> changedNames = result.stream()
     .map(outer -> collectionB.stream()
                               // very slow second full scan comparison
                              .filter(inner -> inner.getKeyA().equals(outer.getKeyA()) 
                                           && inner.getKeyB().equals(outer.getKeyB()) 
                                           && inner.getKeyC().equals(outer.getKeyC()))
                              .findFirst()
                              .map(item -> item.getName()))
     .collect(toSet());
log.info("changed names" + changedNames);

但是我需要重写hashCode和equals,只使用这3个字段,我仍然相信有更优雅的方法,我们可以流化第一个集合,对于每个
A
对象,用分隔符连接
A
的三个字段,并将其收集为一个集合(
set

然后我们检查第二个集合的元素,根据a的键字段组成一个字符串,并检查上面的计算集合是否有它

Set<String> keysOfA = collectionA.stream()
        .map(a -> compose(a.getKeyA(), a.getKeyB(), a.getKeyC()))
        .collect(Collectors.toSet());

Set<String> changedNames = collectionB.stream()
        .filter(b -> !keysOfA.contains(compose(b.getKeyA(), b.getKeyB(), b.getKeyC())))
        .map(b -> b.getName())
        .collect(Collectors.toSet());

static String compose(String keyA, String keyB, String keyC) {
    return keyA + "|" + keyB + "|" + keyC; //any other delimiter would work
}

我们可以流式处理第一个集合,对于每个
A
对象,用分隔符连接
A
的三个字段,并将其收集为一个集合(
set

然后我们检查第二个集合的元素,根据a的键字段组成一个字符串,并检查上面的计算集合是否有它

Set<String> keysOfA = collectionA.stream()
        .map(a -> compose(a.getKeyA(), a.getKeyB(), a.getKeyC()))
        .collect(Collectors.toSet());

Set<String> changedNames = collectionB.stream()
        .filter(b -> !keysOfA.contains(compose(b.getKeyA(), b.getKeyB(), b.getKeyC())))
        .map(b -> b.getName())
        .collect(Collectors.toSet());

static String compose(String keyA, String keyB, String keyC) {
    return keyA + "|" + keyB + "|" + keyC; //any other delimiter would work
}
这是
O(n^2)
,因为您正在为结果中的每个元素流化
collectionB
。以下几点应该可以很快发挥作用:

Set<String> changedNames = collectionB.stream()
                              .filter(b -> collectionA.contains(b.toA())
                              .map(item -> item.getName()).collect(toSet());
Set changedNames=collectionB.stream()
.filter(b->collectionA.contains(b.toA())
.map(item->item.getName()).collect(toSet());
这是
O(n^2)
,因为您正在为结果中的每个元素流式传输
collectionB
。以下操作应该运行得非常快:

Set<String> changedNames = collectionB.stream()
                              .filter(b -> collectionA.contains(b.toA())
                              .map(item -> item.getName()).collect(toSet());
Set changedNames=collectionB.stream()
.filter(b->collectionA.contains(b.toA())
.map(item->item.getName()).collect(toSet());

您的
B.toA()
不会强制转换
B
实例。相反,它创建了一个全新的
a
,其属性值取自
B
。如果可以在这些类型之间进行强制转换,这比强制转换的成本要高得多(事实并非如此)。尽管如此,这可能是一个合理的前进方向,但请确保你理解这里实际发生的事情。确实,但我只是用了错误的词。我编辑了帖子,谢谢。我将
collectionBConvertedToA
的类型编辑为
Set
Your
B.toA()
不会强制转换
B
实例。相反,它创建了一个全新的
a
,属性值取自
B
。如果可以在这些类型之间进行强制转换,则这比强制转换的成本要高得多(事实并非如此).尽管如此,这可能是一个合理的前进方向,但请确保你理解这里实际发生的事情。确实,但我只是用了错误的词。我编辑了帖子,谢谢。我将
collectionBConvertedToA
的类型编辑为
Set
,我不敢相信它这么简单--我会等一会儿,等待更多的回复,但我不认为还有更优雅的smth,谢谢我不敢相信它这么简单-我会等一等更多的回应,但我不认为有smth更优雅,谢谢
Set<String> changedNames = collectionB.stream()
                              .filter(b -> collectionA.contains(b.toA())
                              .map(item -> item.getName()).collect(toSet());