【问题标题】:Merging instances of objects in a list by attribute按属性合并列表中的对象实例
【发布时间】:2021-05-24 19:43:58
【问题描述】:

我有一个对象 Person,它有名字、姓氏和电子邮件

class Person {
   String firstname;
   String lastName;
   String email;
}

我有一个Person 列表,其中可能有多个Persons 相同的firstNamelastName,我想通过他们的电子邮件地址将它们与分隔符合并。

即 人 A =

{
    "firstName": "David",
    "lastName": "Guiney",
    "email": "david.guiney@gmail.com"
}

B 人 =

{
    "firstName": "David",
    "lastName": "Guiney",
    "email": "guiney.david@hotmail.com"
}

我希望将这些合并到

{
    "firstName": "David",
    "lastName": "Guiney",
    "email": "david.guiney@gmail.com;guiney.david@hotmail.com"
}

以便在我的列表中创建一个唯一的实例。

【问题讨论】:

  • stream()filter() 是您的朋友 - 试一试,让我们知道如何提供帮助。
  • 请带上tour,环顾四周,并通读help center,尤其是I ask a good question?What topics can I ask about here? 是如何做到的。从第二个链接:“要求家庭作业帮助的问题必须包括您迄今为止为解决问题所做的工作的摘要,以及您在解决问题时遇到的困难的描述。”即使没有作业。
  • 我投票结束这个问题,因为没有尝试

标签: java collections java-stream


【解决方案1】:
  1. 这取决于您将什么定义为唯一或相等。这可以通过 equals 和 hashCode 方法来表达。

  2. 您可以使用 java.util.stream.Collectors#toMap 方法提供合并功能并将您的列表映射到地图。在合并函数中,您可以实现如何处理具有相同“键”的 2 个对象的逻辑。

    公共类人 { 公共人员(字符串名字,字符串姓氏,字符串电子邮件) { this.firstname = 名字; this.lastName = 姓氏; this.email = 电子邮件; }

     String firstname;
     String lastName;
     String email;
    
     @Override
     public boolean equals(Object o)
     {
         if (this == o)
         {
             return true;
         }
         if (o == null || getClass() != o.getClass())
         {
             return false;
         }
         Person person = (Person) o;
         return Objects.equals(firstname, person.firstname) && Objects.equals(lastName, person.lastName);
     }
    
     @Override
     public int hashCode()
     {
         return Objects.hash(firstname, lastName);
     }
    
     @Override
     public String toString()
     {
         return "Person{" +
             "firstname='" + firstname + '\'' +
             ", lastName='" + lastName + '\'' +
             ", email='" + email + '\'' +
             '}';
     }
    
     public static void main(String[] args)
     {
         List<Person> persons = Arrays.asList(new Person("David", "Guiney", "david.guiney@gmail.com"),
             new Person("David", "Guiney", "david.guiney@gmail.com"),
             new Person("Andreas", "Radauer", "please_no@spam.com")
         );
    
         Map<Integer, Person> uniquePersons =
             persons.stream()
                 .collect(Collectors.toMap(
                     Person::hashCode,
                     Function.identity(),
                     (person1, person2) -> {
                         person1.email = person1.email + ";" + person2.email; // this could be improved
                         return person1;
                     }
                 ));
    
         System.out.println(uniquePersons.values());
     }
    

    }

如果你不想在这个用例中使用 equals 和 hashCode 你当然可以只提供一个自己的 getKey 逻辑

【讨论】:

  • 谢谢安德烈亚斯,这正是我所需要的。太棒了。
【解决方案2】:

您可以一一检查列表中的所有对象,但问题是时间复杂度。它将是 O(n^2) 的形式。

for(int i=0;i<list.size();i++){
        for(int j=i+1;j<list.size();j++){
            if(list.get(i).firstName.equals(list.get(j).firstName) && list.get(i).lastName.equals(list.get(j).lastName)){
                list.get(i).email += ";"+list.get(j).email;
                list.remove(j);
            }
        }
    }

【讨论】:

    【解决方案3】:

    首先,如果一个人可以有几封电子邮件,我会在 Person 类中通过一个“电子邮件”属性来存储电子邮件集合来反映这一点。

    然后你可以使用 Java 8 Streams。

    public class Person {
    
    private String firstName;
    
    private String lastName;
    
    private List<String> emails;// if a person is susceptible to have several emails, use a collection and not a single String
    
    public Person(String firstName, String lastName, String email) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.emails = new ArrayList<>();
        this.emails.add(email);
    }
    
    public static void main(String[] args) {
        List<Person> persons = Arrays.asList(
                new Person("David", "Guiney", "david.guiney@gmail.com"),
                new Person("Jean", "Patrick", "jp@pluton.com"),
                new Person("David", "Guiney", "david.guiney@hotmail.com"),
                new Person("Bruce", "Lee", "bl@kungfu.com")
        );
    
        // Group together the Person instances having the same firstname and same lastname
        Map<List<String>, Person> keyToPerson = persons.stream().collect(Collectors.toMap(
                p -> Arrays.asList(p.firstName, p.lastName),// define the key allowing to group person as the combination of firstname and lastname
                Function.identity(),// what is associated to the key. Here we want the Person instance itself
                (p1, p2) -> {// the logic to solve collision, i.e. instances having the same key
                    p1.emails.addAll(p2.emails);// add the email(s) of the second instances in the email of the first instance
                    return p1;
                }
        ));
        keyToPerson.values().forEach(p -> {
            System.out.println(p.firstName + " " + p.lastName + " (" + String.join("; ", p.emails) + ")");
        });
    }
    

    }

    它应该像这样打印一些想法

    David Guiney (david.guiney@gmail.com; david.guiney@hotmail.com)
    Bruce Lee (bl@kungfu.com)
    Jean Patrick (jp@pluton.com)
    

    您还可以重新定义 Person 类的 equals 和 hashcode 方法来实现如何将 2 个实例组合在一起。

    【讨论】:

    • 谢谢,在 Person 对象中使用电子邮件列表将是一个理想的解决方案,但不幸的是,进行该更改会破坏许多其他代码。 (我正在使用的实际对象是一个更复杂的模型)。上面的第一个响应给出了对我有用的理想解决方案。
    猜你喜欢
    • 2022-11-15
    • 2012-06-14
    • 2017-06-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-12
    • 2015-12-05
    相关资源
    最近更新 更多