我需要将 listB 的所有元素合并到另一个列表 listA 中。
如果 listA 中已存在元素(基于自定义等式检查),我不想添加它。
我不想使用Set,我不想覆盖equals()和hashCode()。
原因是,我不想防止listA本身出现重复,如果listA中已经存在我认为相同的元素,我只想不从listB合并。
我不想覆盖equals()和hashCode(),因为这意味着我需要确保,我在各种情况下都能保持元素的equals()实现。然而,它可能是listB中的元素没有完全初始化,即它们可能会遗漏一个对象id,其中可能存在于listA的元素中。
我目前的方法涉及一个界面和一个效用函数:
public interface HasEqualityFunction<T> {
public boolean hasEqualData(T other);
}
public class AppleVariety implements HasEqualityFunction<AppleVariety> {
private String manufacturerName;
private String varietyName;
@Override
public boolean hasEqualData(AppleVariety other) {
return (this.manufacturerName.equals(other.getManufacturerName())
&& this.varietyName.equals(other.getVarietyName()));
}
// ... getter-Methods here
}
public class CollectionUtils {
public static <T extends HasEqualityFunction> void merge(
List<T> listA,
List<T> listB) {
if (listB.isEmpty()) {
return;
}
Predicate<T> exists
= (T x) -> {
return listA.stream().noneMatch(
x::hasEqualData);
};
listA.addAll(listB.stream()
.filter(exists)
.collect(Collectors.toList())
);
}
}
然后我就这样使用它:
...
List<AppleVariety> appleVarietiesFromOnePlace = ... init here with some elements
List<AppleVariety> appleVarietiesFromAnotherPlace = ... init here with some elements
CollectionUtils.merge(appleVarietiesFromOnePlace, appleVarietiesFromAnotherPlace);
...
在listA中获取我的新列表,其中所有元素都从B合并。
这是一个好方法吗?是否有更好/更容易的方法来完成相同的工作?
答案 0 :(得分:7)
你想要这样的东西:
public static <T> void merge(List<T> listA, List<T> listB, BiPredicate<T, T> areEqual) {
listA.addAll(listB.stream()
.filter(t -> listA.stream().noneMatch(u -> areEqual.test(t, u)))
.collect(Collectors.toList())
);
}
您不需要HasEqualityFunction
界面。您可以重用BiPredicate
来测试两个对象在逻辑上是否相等。
此代码仅根据给定的谓词过滤listB
中未包含在listA
中的元素。它与listA
中的元素一样遍历listB
次。
另一种更好的性能实现方法是使用包装元素的包装类,并使用equals
方法作为谓词:
public static <T> void merge(List<T> listA, List<T> listB, BiPredicate<T, T> areEqual, ToIntFunction<T> hashFunction) {
class Wrapper {
final T wrapped;
Wrapper(T wrapped) {
this.wrapped = wrapped;
}
@Override
public boolean equals(Object obj) {
return areEqual.test(wrapped, ((Wrapper) obj).wrapped);
}
@Override
public int hashCode() {
return hashFunction.applyAsInt(wrapped);
}
}
Set<Wrapper> wrapSet = listA.stream().map(Wrapper::new).collect(Collectors.toSet());
listA.addAll(listB.stream()
.filter(t -> !wrapSet.contains(new Wrapper(t)))
.collect(Collectors.toList())
);
}
首先将Wrapper
对象中的每个元素包装起来,然后将它们收集到Set
中。然后,它会过滤此集合中未包含的listB
元素。通过委托给定谓词来完成相等性测试。限制是我们还需要hashFunction
来正确实施hashCode
。
示例代码为:
List<String> listA = new ArrayList<>(Arrays.asList("foo", "bar", "test"));
List<String> listB = new ArrayList<>(Arrays.asList("toto", "foobar"));
CollectionUtils.merge(listA, listB, (s1, s2) -> s1.length() == s2.length(), String::length);
System.out.println(listA);
答案 1 :(得分:2)
您可以使用HashingStrategy
中基于Eclipse Collections的Set
如果您可以使用MutableList界面:
public static void merge(MutableList<AppleVariety> listA, MutableList<AppleVariety> listB)
{
MutableSet<AppleVariety> hashingStrategySet = HashingStrategySets.mutable.withAll(
HashingStrategies.fromFunctions(AppleVariety::getManufacturerName,
AppleVariety::getVarietyName),
listA);
listA.addAllIterable(listB.asLazy().reject(hashingStrategySet::contains));
}
如果您无法从List
更改listA和listB的类型:
public static void merge(List<AppleVariety> listA, List<AppleVariety> listB)
{
MutableSet<AppleVariety> hashingStrategySet = HashingStrategySets.mutable.withAll(
HashingStrategies.fromFunctions(AppleVariety::getManufacturerName,
AppleVariety::getVarietyName),
listA);
listA.addAll(ListAdapter.adapt(listB).reject(hashingStrategySet::contains));
}
注意:我是Eclipse Collections的撰稿人。