从 Java 8 中基于多个属性的对象列表中删除重复项

问题描述 投票:0回答:5

我想比较 getCode 和 getMode 并找到重复的记录。

然后还有一个产品属性 getVode,它在两条记录中始终具有不同的值(true 或 false)。

P1   getCode  getMode  getVode
1    001      123      true
P2   getCode  getMode  getVode
2    001      123      false

我在下面尝试过,但它只找到重复项:

List<ProductModel> uniqueProducts = productsList.stream()
    .collect(Collectors.collectingAndThen(
        toCollection(() -> new TreeSet<>(
            Comparator.comparing(ProductModel::getCode)
                .thenComparing(ProductModel::getMode)
        )),
        ArrayList::new));

因此,当我发现重复记录时,我想检查 getVode 值是否为 false 并将其从列表中删除。 任何帮助将不胜感激?

java java-8 java-stream
5个回答
4
投票

据我了解,只有当元素是重复的并且它们的getVode

方法返回
false
时,你才想删除它们。

我们可以从字面上做到这一点。首先,我们必须确定哪些元素是重复的:

Map<Object, Boolean> isDuplicate = productsList.stream() .collect(Collectors.toMap(pm -> Arrays.asList(pm.getCode(), pm.getMode()), pm -> false, (a, b) -> true));

然后,删除满足条件的:

productsList.removeIf(pm -> !pm.getVode() && isDuplicate.get(Arrays.asList(pm.getCode(), pm.getMode())));

或者,不修改旧列表:

List<ProductModel> uniqueProducts = new ArrayList<>(productsList); uniqueProducts.removeIf(pm -> !pm.getVode() && isDuplicate.get(Arrays.asList(pm.getCode(), pm.getMode())));

也可以通过Stream操作来完成:

List<ProductModel> uniqueProducts = productsList.stream() .filter(pm -> pm.getVode() || !isDuplicate.get(Arrays.asList(pm.getCode(), pm.getMode()))) .collect(Collectors.toList());
    

3
投票
在这里,您可以删除重复项,无论

getVode()

 值是多少,因为传递给 
Comparator
TreeSet
 中不考虑它。
你的方法并不容易。
您可以根据可以用
Map<ProductModelId, List<ProductModelId>>
 类表示的 
getCode()
getMode()
 值对元素进行分组来创建 
ProductModelId
然后对于 Map 的每个条目进行处理:如果列表包含单个元素,则保留它,否则不要保留所有
getVode()
 为 false 的元素。   

Map<ProductModelId, List<ProductModel>> map = productsList.stream() .collect(groupingBy(p -> new ProductModelId(p.getCode(), p.getMode()); List<ProductModel> listFiltered = map.values() .stream() .flatMap(l -> { if (l.size() == 1) { return Stream.of(l.get(0)); } else { return l.stream().filter(ProductModel::getVode); } } ) .collect(toList());

请注意,

ProductModelId

 应通过考虑两个字段的值来覆盖 
equals/hashCode
,以便在地图中将它们正确分组:

public class ProductModelId { private String code; private String mode; public ProductModelId(String code, String mode) { this.code = code; this.mode = mode; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof ProductModelId)) return false; ProductModelId that = (ProductModelId) o; return Objects.equals(code, that.code) && Objects.equals(mode, that.mode); } @Override public int hashCode() { return Objects.hash(code, mode); } }
    

0
投票
您可以按代码和模式的组合进行分组。然后在 mergeFunction 中获取带有

true

 vode:
的元素

Collection<ProductModel> uniqueProducts = products.stream() .collect(toMap( p -> Arrays.asList(p.getCode(), p.getMode()), Function.identity(), (p1, p2) -> p1.getVode() ? p1 : p2)) .values();

参见

javaDoc for toMap


0
投票
如果您的

vode

 可以是 
true
 的多个 
ProductModel
 实例(否则,如果您期望单个 
true
 - 这更简单,我将让这个练习给您)并且您想保留它们所有,也许这就是您所追求的:

List<ProductModel> models = List.of( new ProductModel(1, 123, false), new ProductModel(1, 123, true)); // just an example Map<List<Integer>, List<ProductModel>> map = new HashMap<>(); models.forEach(x -> { map.computeIfPresent(Arrays.asList(x.getMode(), x.getCode()), (key, value) -> { value.add(x); value.removeIf(xx -> !xx.isVode()); return value; }); map.computeIfAbsent(Arrays.asList(x.getMode(), x.getCode()), key -> { List<ProductModel> list = new ArrayList<>(); list.add(x); return list; }); }); map.values() .stream() .flatMap(List::stream) .forEachOrdered(x -> System.out.println(x.getCode() + " " + x.getMode()));

其中

ProductModel

 类似于:

static class ProductModel { private final int code; private final int mode; private final boolean vode; // some other fields, getters, setters }

这并不是那么容易实现的。您首先需要查找是否存在重复项,只有在发现重复项时才采取相应措施。

map.computeIfAbsent

 小心地放入地图键(
Key
 是由 
Code/Mode
 包裹在 
Arrays::asList
 中制成的 - 它正确地覆盖了 
hashCode/equals
)。

当根据该密钥找到重复项时,我们希望通过

map.computeIfPresent

 采取行动。考虑到 
vode
 可以在多个实例中成为 
true
(是的,这是我的假设),“表演”也不是微不足道的。您不知道之前的钥匙在这张地图中放入了什么 
vode
 - 是 
false
 吗?如果是这样,则必须将其删除。但现在的
false
也是吗?如果是这样,也必须将其删除。 


0
投票
Map uniqueList = new HashMap(); for (Map elem : elemList) { String key = (String) elem .get("xx") + (String) elem .get("yy") + (String) elem .get("zz"); uniqueList.put(key, service); } return new ArrayList<Map>(uniqueList.values());
    
© www.soinside.com 2019 - 2024. All rights reserved.