Lambda 如何流2个列表&;修改1中的值

Lambda 如何流2个列表&;修改1中的值,lambda,java-8,java-stream,Lambda,Java 8,Java Stream,我想迭代2个列表来更新1中的值。下面是我尝试使用嵌套for循环的简化版本 注释掉的代码是我在尝试利用流时得到的。假设姓氏是唯一的。我将在实际代码中比较的值实际上是唯一的 如果有人能给我指出正确的方向,我将不胜感激 List<Person> list1 = new ArrayList<>(); List<Person> list2 = new ArrayList<>(); list1.add(new Person("Jane", "Doe"));

我想迭代2个列表来更新1中的值。下面是我尝试使用嵌套for循环的简化版本

注释掉的代码是我在尝试利用流时得到的。假设姓氏是唯一的。我将在实际代码中比较的值实际上是唯一的

如果有人能给我指出正确的方向,我将不胜感激

List<Person> list1 = new ArrayList<>();
List<Person> list2 = new ArrayList<>();

list1.add(new Person("Jane", "Doe"));
list1.add(new Person("John", "Smith"));

list2.add(new Person("", "Doe"));
list2.add(new Person("", "Smith"));

for (Person p1 : list1) {
    for (Person p2 : list2) {
        if (p1.getLastName().equals(p2.getLastName())) {
            p2.setFirstName(p1.getFirstName());
            break;
        }
    }
}

System.out.println(list2.toString());

//list1.stream()
//  .filter(e -> (list2.stream()
//          .filter(d -> d.getLastName()
//          .equals(e.getLastName()))
List list1=new ArrayList();
List list2=新的ArrayList();
列表1.添加(新的人(“简”、“Doe”);
列表1.添加(新人(“约翰”、“史密斯”);
清单2.添加(新人员(“,“Doe”);
列表2.添加(新人员(“,”史密斯”);
用于(人员p1:list1){
对于(人员p2:list2){
如果(p1.getLastName().equals(p2.getLastName())){
p2.setFirstName(p1.getFirstName());
打破
}
}
}
System.out.println(list2.toString());
//列表1.stream()
//.filter(e->(list2.stream())
//.filter(d->d.getLastName()
//.equals(e.getLastName())
您可以创建一个
映射,并使用它来更新第二个列表:

Map<String, String> lastNameMap = list1.stream()
        .collect(Collectors.toMap(Person::getLastName, Person::getFirstName));
list2.stream().forEach(p -> p.setFirstName(lastNameMap.get(p.getLastName())));
Map lastNameMap=list1.stream()
.collect(Collectors.toMap(Person::getLastName,Person::getFirstName));
list2.stream().forEach(p->p.setFirstName(lastNameMap.get(p.getLastName()));
请注意,这假设没有重复的姓氏,任何重复的姓氏都可能导致混淆


由于上面编写了collect调用,重复的
lastName
值会导致调用失败,可能应该保持原样,这样错误就可以防止姓/名混淆。

这里是另一种方法:

 list1.forEach(p1 -> list2.stream()
                          .filter(p2 -> p1.getLastName().equals(p2.getLastName()))
                          .findFirst()
                          .ifPresent(p2 -> p2.setFirstName(p1.getFirstName()))
              );
或:


经典的
for
循环似乎是最好的选择。流
解决方案如下:

list1.forEach(p1 ->
    list2.stream().filter(p2 -> p1.getLastName().equals(p2.getLastName()))
                  .findFirst()
                  .ifPresent(p2 -> p2.setFirstName(p1.getFirstName())));

当你想用streams解决你的问题时,我想你想深入函数式编程。如果是这样的话,我建议你退一步,试着将函数式编程的基本原理内化,这样你就可以开始用函数式编程的思维来处理这样的问题。在这种情况下,这意味着s:不要就地更新数据结构,而是返回具有更新值的新实例(列表和个人实体均为true),并避免运行有副作用的代码块。始终从操作中返回有意义的值

考虑到这一点,我建议采用以下方法:

final List<Person> combined = list1.stream()
  .flatMap(p1 -> list2.stream().filter(p2 -> p2.lastname().equals(p1.lastName())).map(p2 -> new Person(p1.firstName(), p2.lastName())))
  .collect(Collectors.toList())
final List combined=list1.stream()
.flatMap(p1->list2.stream().filter(p2->p2.lastname().equals(p1.lastname())).map(p2->newperson(p1.firstName(),p2.lastname()))
.collect(收集器.toList())

也许您可以以此为起点。

您可能需要检查
lastNameMap
是否包含我们正在查找的lastNamefor@user7很好。我选择让代码设置
firstName
null
,如果在map中找不到姓氏值……或者让它保持不变,lambda可以更改为
p->p.setLastName(lastNameMap.getOrDefault(p,“”)
如果我们需要使用现有OP的行为,那么当前值必须不受影响(null或“”)因为OP说姓氏是唯一的,但我没有看到任何证据表明列表条目是1-1.+1anyway@user7同意。但是在仅仅为了过滤而查找地图两次和将“名字”设置为默认值(根据问题是
)之间,我会选择后者,尽管我明白你的意思。我正在循环两个不同的对象。Person对象用于简化。如果函数式编程需要创建新对象,那么经典的for循环就足够了。@BryanJ我建议不要使用streams,尤其是lambdas(误导性地)表示您正在使用纯函数,但实际情况并非如此。这更多是关于样式/惯例
final List<Person> combined = list1.stream()
  .flatMap(p1 -> list2.stream().filter(p2 -> p2.lastname().equals(p1.lastName())).map(p2 -> new Person(p1.firstName(), p2.lastName())))
  .collect(Collectors.toList())