如果Person存在,则更新Set<Person>,如果它们不存在,则添加它们。

4
假设我有一个非常简单的类Person,它包含姓名和地址:
public class Person {
    String name;
    String address;

    public Person(final String name,
                  final String address) {
        this.name = name;
        this.address = address;
    }

    public String getName() {
        return name;
    }

    public void setName(final String newName) {
        this.name = newName;
    }

    public String address() {
        return address;
    }

    public void setAddress(final String newAddress) {
        this.address = newAddress;
    }
}

我现在有一个包含所有已知Person对象的Set,我想仅根据他们的姓名更新其中一个人的地址。为此,我有以下内容:

persons.stream()
       .filter(person -> personToUpdate.getName().equalsIgnoreCase(person.getName()))
       .forEach(person -> {
                person.setAddress(personToUpdate.getAddress());
        });

然而,我的问题是当我有一个全新的人员不在集合中时。如何检查集合以查看该人是否存在,并且如果存在,则更新其地址。但是,如果他们不存在,则将其添加到列表中。我知道这很简单,但出于某种原因,我现在无法想到如何实现。我不想走创建所有名称列表的路线,比较新名称,是否在列表中等等。我宁愿保持尽可能简洁。编辑:名称将是唯一的!

1
有什么特别的原因要使用 Set 而不是 Map 吗? - Joe C
1
只会有零个或一个匹配的“Person”,还是可能会有多个?此外,您似乎没有在“Person”上实现“equals”或“hashCode”,那么您的“Set”如何区分“Person”对象?仅通过引用相等性吗? - Daniel Pryden
5个回答

5

除非你需要一行代码,否则你可以利用 Optional 来简单实现:

Optional<Person> person = persons.stream()
        .filter(p -> personToUpdate.getName().equalsIgnoreCase(p.getName()))
        .findFirst();

if (person.isPresent()) {
    person.get().setAddress(personToUpdate.getAddress());
} else {
    persons.add(personToUpdate);
}

如果你使用的是Java 9及以上版本,这会看起来更好:
persons.stream()
        .filter(p -> personToUpdate.getName().equalsIgnoreCase(p.getName()))
        .findFirst()
        .ifPresentOrElse(
            p -> p.setAddress(personToUpdate.getAddress()),
            () -> persons.add(personToUpdate)
        );

当然,这假设名称是唯一的(因为您希望更新一个条目)。

在使用流时更新列表看起来不太对,应该避免这样做。 - Naman
1
我的意思是,JIT 可能会优化掉这里 Optional 实例的分配,但这真的比使用 for 循环的死简单方式更好吗?它同样多,如果不是更多的代码行,而且密度也到了几乎一眼难以理解的地步,没有实际的好处来弥补它。 - Daniel Pryden

1
你可以通过使用 Map 这样做。
Map<String, Person> personMap = persons.stream()
    .collect(Collectors.toMap(Person::getName, Function.identity()));
personMap.merge(personToUpdate.getName(), personToUpdate, 
    (p1, p2) -> new Person(p1.getName(), p2.address()));

0
假设(1)您的Person对象仅使用引用相等进行比较(因此需要保留对象标识),并且(2)多个Person对象可能匹配给定名称,您需要手动处理这些情况:
public static void updatePersonAddress(String name, String address, Set<Person> persons) {
    // Find all the matching persons, if any.
    List<Person> matched = new ArrayList<>();
    for (Person person : persons) {
        if (person.getName().equalsIgnoreCase(name)) {
            matched.append(person);
        }
    }

    // If zero persons matched, create a new Person to match,
    // and add the new person to the set. Then we're done!
    if (matched.isEmpty()) {
        persons.add(new Person(name, address));
        return;
    }

    // Otherwise, update the Person objects (which are still in the
    // persons Set) to include the new address.
    for (Person person : matched) {
        person.setAddress(address);
    }
}

如果名称是唯一的,这将简化为:
public static void updatePersonAddress(String name, String address, Set<Person> persons) {
    for (Person person : persons) {
        if (person.getName().equalsIgnoreCase(name)) {
            person.setAddress(address);
            // If we've found one that matches, then we know there
            // won't be any more, so we can be done now.
            return;
        }
    }
    // If we made it through the loop without returning, then it means
    // that no Person in the Set matched. So add one that does.
    persons.add(new Person(name, address));
}

0

有几种简单的方法可以做到这一点(我假设名称唯一地标识一个人,并且具有相同名称的两个人对象被视为相等)

  1. 实现 equalshashcode,并在流之前进行 contains 调用 - 如果返回 false,则不在集合中
  2. 使用 Map 而不是 Set,其中键可以轻松地用于按属性查找人员(例如名称,在这里似乎是您的主键)。

顺便说一句 - 无论如何都应该实现 equalshashcode - 特别是如果您将这些对象放入 SetMap 中。通过仅考虑名称来实现 equalshashcode,您将确保集合内的名称唯一性(现在未保证)


请注意,OP只有一个名称可供参考,因此在这里使用equalshashCode可能效果不佳。 - Joe C
在这个系统中,如果两个 Person 对象具有相同的名称但不同的地址,它们是否相等?根据他们想要使用 Person 的方式,似乎是这种情况。并不是说这是最好的方法(这就是为什么真实的人有其他区分因素,比如社会安全号码)。 - Krease
不好意思,不是这样的。我需要检查Set中是否有一个包含与我需要更新的相同姓名的Person对象。 - MeanwhileInHell
我需要检查Set中是否有一个Person对象,其包含与我需要更新的相同名称。这正是我的意思 - 如果找到一个具有匹配名称的Person,它就等于您要更新的那个。 - Krease

0
你可以使用 persons.contains(person)。但是你需要重写 hashcode() 和 equals() 方法。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接