使用Java 8

时间:2017-06-09 16:08:50

标签: arraylist java-8 java-stream

如果这是一个我应该知道的简单基本信息,我很抱歉。这是我第一次尝试使用Java 8流和其他功能。

我有两个包含相同类型对象的ArrayLists。我们说list1list2。假设列表中的Person对象具有属性“ employeeId ”。

场景是我需要合并这些列表。但是,list2可能有一些与list1中相同的对象。因此,我尝试从list2中删除与list1中相同的对象,并获取结果列表,然后我可以在list1中合并。

我正在尝试使用Java 8 removeIf() stream()功能。以下是我的代码:

public List<PersonDto> removeDuplicates(List<PersonDto> list1, List<PersonDto> list2) {
        List<PersonDto> filteredList = list2.removeIf(list2Obj -> {
            list1.stream()
                .anyMatch( list1Obj -> (list1Obj.getEmployeeId() == list2Obj.getEmployeeId()) );
        } );
    }

上面的代码给出了编译错误,如下所示:

类型Collection中的removeIf(Predicate)方法不适用于参数((list2Obj) - &gt; {})

所以我将“ removeIf()”开头的list2Obj更改为(<PersonDto> list2Obj),如下所示:

public List<PersonDto> removeDuplicates(List<PersonDto> list1, List<PersonDto> list2) {
            List<PersonDto> filteredList = list2.removeIf((<PersonDto> list2Obj) -> {
                list1.stream()
                    .anyMatch( list1Obj -> (list1Obj.getEmployeeId() == list2Obj.getEmployeeId()) );
            } );
        }

这给我一个错误如下:

令牌“&lt;”上的语法错误,删除此令牌以查看(<PersonDto> list2Obj)中的“&lt; ”和令牌上的语法错误( s),来自' - &gt;的部分错位的构造 {

我对我真正需要做的事情感到茫然。

如果有人可以帮我解决这个问题,我将不胜感激。

2 个答案:

答案 0 :(得分:2)

我简化了你的功能,使其更具可读性:

public static List<PersonDto> removeDuplicates(List<PersonDto> left, List<PersonDto> right) {
    left.removeIf(p -> {
        return right.stream().anyMatch(x -> (p.getEmployeeId() == x.getEmployeeId()));
    });
    return left;
}

另请注意,您要修改left参数,而不是创建新的List

你也可以使用:left.removeAll(right),但是你需要equalshashcode,而你似乎没有它们;或者它们基于employeeId之外的其他内容。

另一种选择是将这些列表收集到TreeSet并使用removeAll

TreeSet<PersonDto> leftTree = left.stream()
      .collect(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(PersonDto::getEmployeeId))));

TreeSet<PersonDto> rightTree = right.stream()
      .collect(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(PersonDto::getEmployeeId))));

leftTree.removeAll(rightTree);

答案 1 :(得分:2)

据我所知,您正在尝试合并两个列表而不复制属于交叉点的元素。有很多方法可以做到这一点。一种是您尝试过的方式,即从一个列表中删除属于另一个列表的元素,然后合并。反过来,这可以通过多种方式完成。

其中一种方法是将一个列表的员工ID保存在HashSet中,然后在另一个列表中使用removeIf,并使用谓词检查每个元素是否具有该员工ID包含在集合中。这比在第一个列表的每个元素的第二个列表上使用anyMatch更好,因为HashSet.containsO(1)摊销时间内运行。这是解决方案的草图:

// Determine larger and smaller lists
boolean list1Smaller = list1.size() < list2.size();
List<PersonDto> smallerList = list1Smaller ? list1 : list2;
List<PersonDto> largerList = list1Smaller ? list2 : list1;

// Create a Set with the employee ids of the larger list
// Assuming employee ids are long
Set<Long> largerSet = largerList.stream()
    .map(PersonDto::getEmployeeId)
    .collect(Collectors.toSet());

// Now remove elements from the smaller list
smallerList.removeIf(dto -> largerSet.contains(dto.getEmployeeId()));

这背后的逻辑是HashSet.contains对于大型和小型集合将花费相同的时间,因为它在O(1)摊销时间内运行。但是,遍历列表并从中删除元素在较小的列表上会更快。

然后,您已准备好合并两个列表:

largerList.addAll(smallerList);