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

时间:2019-06-28 10:21:09

标签: java java-8 java-stream

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

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

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));

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

4 个答案:

答案 0 :(得分: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());

答案 1 :(得分:3)

在这里,您将删除重复项,无论getVode()值如何,因为传递给Comparator的{​​{1}}中都不会考虑它。
您的方法不容易。
您可以根据元素的TreeSetMap<ProductModelId, List<ProductModelId>>值(可以用getCode()类表示)来对元素进行分组来创建getMode()
然后对于Map进程的每个条目:如果列表包含单个元素,请保留它,否则不要将所有ProductModelId都设置为false的元素。

getVode()

请注意,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

答案 2 :(得分: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

答案 3 :(得分: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也是吗?如果是这样,还必须将其删除。