【问题标题】:Get unique user defined objects from two java lists using java8使用 java8 从两个 java 列表中获取唯一的用户定义对象
【发布时间】:2018-05-05 18:46:34
【问题描述】:

我有一个名为 Employee 的用户定义对象,如下所示,并且有两个包含 Employee 对象的不同列表。

在两个不同的列表中,我需要根据对象中的名称字段找出唯一的对象。 最终列表应仅包含一个名为 c 的对象。

请建议我如何使用 java 8 做到这一点?

import java.util.ArrayList;
import java.util.List;

class Employee{
    private String name;
    private String age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getAge() {
        return age;
    }
    public void setAge(String age) {
        this.age = age;
    }   
}


public class UniqueObjects {

    public static void main(String[] args) {
        List<Employee> empOneList= new ArrayList<Employee>();
        List<Employee> empTwoList= new ArrayList<Employee>();

        Employee empOne= new Employee();        
        empOne.setName("a");
        empOne.setAge("23");
        empOneList.add(empOne);

        Employee emptwo= new Employee();        
        emptwo.setName("b");
        emptwo.setAge("24");
        empOneList.add(emptwo);

        Employee em= new Employee();
        em.setName("a");
        em.setAge("23");
        empTwoList.add(em);

        Employee emp1= new Employee();
        emp1.setName("d");
        emp1.setAge("24");
        empTwoList.add(emp1);    
    }
}

【问题讨论】:

  • 当两个起始列表都没有这个名字时,为什么最终列表应该有名字c
  • @TimBiegeleisen 我这样理解他正在搜索的对象的名称是c 而不是列表
  • 在上面的例子中,唯一列表应该只包含一个 Employee 对象。如果我们有多个关于 name 字段的唯一元素,最终列表也应该包含 Employee 对象的列表
  • @Raju,您能否在代码中给出的示例中添加结果应该类似的内容,以明确您想要实现的目标?结果是否应该包含员工“b”和“d”?
  • 两个不同的员工可以有相同的名字。 35 岁的 Joe 与 20 岁的 Joe 不同。但如果是大公司,你可能会有两个 20 岁的 Joe。

标签: java collections java-stream


【解决方案1】:

我认为您的问题是如何在两个列表中的任何一个中找到具有给定姓名的单个员工。如果是这种情况,那么最简单的方法就是流式传输两个列表并过滤唯一员工:

Optional<Employee> employee = Stream.concat(list1.stream(), list2.stream())
    .filter(e -> e.getName().equals(name)).findAny();

如果您想要两个列表中具有名称的对象,那么:

List<Employee> employees = Stream.concat(list1.stream(), list2.stream))
    .filter(e -> e.getName().equals(name)).collect(Collectors.toList());

【讨论】:

  • 您好,感谢您的回答,我需要不应该具有相同名称的对象列表。
  • @Raju 我很困惑。在您的评论中,您说您想要“不应具有相同名称的对象列表”,但在您的问题中您说“列表应仅包含一个名称为 c 的对象”。你的目标到底是什么?
【解决方案2】:

要根据名称字段找出唯一对象,您可以使用以下代码:

Map<String, List<Employee>> employeeMap = Stream.concat(empOneList.stream(), empTwoList.stream())
                .collect(Collectors.groupingBy(Employee::getName, Collectors.toList()));
  1. 使用番石榴

    Collection<List<Employee>> employeesWithUniqueName =
            Maps.filterValues(employeeMap, empList -> empList.size() == 1).values();
    
  2. 使用流 API

    Collection<Employee> employeesWithUniqueName =
            employeeMap.values().stream().filter(empList -> empList.size() == 1)
                    .flatMap(List::stream)
                    .collect(Collectors.toList());
    

【讨论】:

    【解决方案3】:

    如果我正确理解您的问题,您正在寻找仅出现在其中一个列表中而不出现在两个列表中的元素。这称为disjunctive union。使用Collection.removeAll() 有一个非常简单的方法来检索它:

    public static <T> Set<T> getDisjunctiveUnion(Set<T> set1, Set<T> set2)
    {
        Collection<T> copy1 = new HashSet<>(set1);
        Collection<T> copy2 = new HashSet<>(set2);
    
        copy1.removeAll(set2);
        copy2.removeAll(set1);
    
        copy1.addAll(copy2);
        return copy1;
    }
    

    请注意,这需要基于nameEmployee 实现equalshashCode(您的IDE 将为您执行此操作):

    @Override
    public int hashCode()
    {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }
    
    @Override
    public boolean equals(Object obj)
    {
        if (this == obj) return true;
        if (obj == null) return false;
        if (getClass() != obj.getClass()) return false;
        Employee other = (Employee) obj;
        if (name == null && other.name != null) return false;
        else if (!name.equals(other.name)) return false;
        return true;
    }
    

    如果不想实现这些方法,可以使用新方法Collection.removeIf()

    copy1.removeIf(emp1 -> set2.stream().anyMatch(emp2 -> emp2.getName().equals(emp1.getName())));
    copy2.removeIf(emp2 -> set1.stream().anyMatch(emp1 -> emp1.getName().equals(emp2.getName())));
    

    或者,使用流来过滤集合而不是复制/删除(注意使用noneMatch 而不是anyMatch):

    Collection<Employee> disjunctiveUnion = new HashSet<>();
    set1.stream()
        .filter(emp1 -> set2.stream().noneMatch(emp2 -> emp2.getName().equals(emp1.getName())))
        .forEach(disjunctiveUnion::add);
    set2.stream()
        .filter(emp2 -> set1.stream().noneMatch(emp1 -> emp1.getName().equals(emp2.getName())))
        .forEach(disjunctiveUnion::add);
    return disjunctiveUnion;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-14
      • 1970-01-01
      • 2020-05-15
      • 1970-01-01
      相关资源
      最近更新 更多