Java 比较两组不同类型数据的有效方法
首先,我需要一些非常有效的解决方案,因为我正在比较>300k元素的集合 一开始我们有两门不同的课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);
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
YourB.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());