当前位置: 首页 > 知识库问答 >
问题:

使用java8流从另一个列表创建对象列表

吕晟睿
2023-03-14

我有一个学生名单a和学生名单B。

学生对象包含如下字段:否、年龄、城市、出生日期、工资

我的列表A包含这些对象

A=[1 , 20 , Rome ,    2001, 300 ]
  [2 , 21 , Dublin ,  2005, 250 ]
  [3 , 24 , Istanbul, 2012, 350 ]
  [4 , 27 , Madrid ,  2002, 450 ]
  [5 , 27 , London ,  2005, 650 ]

我的列表B包含这些对象

B=[1,  20 , Rome ,    2014, 700 ]
  [3,  24 , Istanbul, 2012, 350 ]
  [4,  27 , Madrid,   2009, 450 ]

我想做的是提取ListA有但listB没有的学生对象,以及ListA和listB有但薪水不同的学生对象(如否、年龄、城市)。我还想写工资差异。

 Result set = [5 , 27 ,London, 2005, 650 ]
              [2 , 21 ,Dublin ,2005, 250 ]
              [1,  20, Rome ,  2001, -400]

我想在java 8中使用流api。首先,我想将students对象提取到列表中,但我现在可以提取常见的student对象。我尝试使用noneMatch,但它不起作用。你知道吗?

List<Student> listDiffList = A.stream()
            // We select any elements such that in the stream of elements from the second list
            .filter(two -> B.stream()
            // there is an element that has the same name and school as this element,
                .anyMatch(one -> one.getNo().equals(two.getNo()) 
                        && one.getAge().equals(two.getAge())
                        && one.getCity().equals(two.getCity())))
            // and collect all matching elements from the first list into a new list.
            .collect(Collectors.toList());

共有2个答案

公良修竹
2023-03-14

如果您有两个列表,您只想要一个从列表A中减去列表B的结果列表,最明显的方法是使用List接口的RemoveAll()方法:

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

public class Test {
  public static void main(String[] args){

    List<Integer> listA = List.of(Integer.valueOf(1),
                                  Integer.valueOf(2),
                                  Integer.valueOf(3),
                                  Integer.valueOf(4),
                                  Integer.valueOf(5));

    List<Integer> listB = List.of(Integer.valueOf(1),
                                  Integer.valueOf(3),
                                  Integer.valueOf(4));

    List<Integer> listResult = 
             new ArrayList<>(listA);  // creating an identical list
    listResult.removeAll(listB);      // subtract the elements present in list B

    System.out.println("listA: " + listA);
    System.out.println("listB: " + listB);
    System.out.println("listA \"minus\" listB: " + listResult);
    System.out.println();
  }
}

将打印:

listA: [1, 2, 3, 4, 5]
listB: [1, 3, 4]
listA "minus" listB: [2, 5]

您可以使用流接口来完成它,但它没有那么简单。但是anyMatch()noneMatch()都可以在这里工作。它们都是返回布尔值的“短路终端操作[s]”,因此它们作为谓词是完美的。只需流式传输您的列表A并使用带有谓词的过滤器来删除您不想要的元素。对于anyMatch(),您将希望过滤掉结果是否为真,因此需要用not颠倒。noneMatch()非常简单:

    List<Integer> listResultStream = 
      listA.stream()

..然后使用以下任一选项:

           .filter(intA -> 
                     !(listB.stream()
                            .anyMatch(intB -> intB.equals(intA))))

..或:

           .filter(intA -> 
                     listB.stream()
                          .noneMatch(intB -> intB.equals(intA)))

并收集结果:

           .collect(Collectors.toList());

对于您的特定场景,我可能不会使用streams解决方案。尽管如此,我后来还是添加了一些。

我用过这个学生班。我决定注释掉出生日期,因为它与解决方案无关:

class Student {
  int       no;
  int       age;
  String    city;
//  LocalDate birth_date;
  int       salary;

//  Student(int no, int age, String city, int birth_date, int salary){
  Student(int no, int age, String city, int salary){
    this.no = no;
    this.age = age;
    this.city = city;
//    this.birth_date = LocalDate.of(birth_date, 1, 1); // January 1st.
    this.salary = salary;
  }

  @Override
  public String toString(){
    return "(" +
           no + ", " +
           age + ", " + 
           city + ", " +
//           birth_date + ", " +
           salary +
           ")";
  }

由于没有出生日期,我将这些记录用于两个列表:

    List<Student> listStudentA = List.of(new Student(1 , 20 , "Rome" ,    300 ),
                                         new Student(2 , 21 , "Dublin" ,  250 ),
                                         new Student(3 , 24 , "Istanbul", 350 ),
                                         new Student(4 , 27 , "Madrid" ,  450 ),
                                         new Student(5 , 27 , "London" ,  650 ),
                                         new Student(6 , 27 , "Paris" ,   650 ));
   
    List<Student> listStudentB = List.of(new Student(1 , 20 , "Rome" ,    700 ),
                                         new Student(3 , 24 , "Istanbul", 350 ),
                                         new Student(4 , 27 , "Madrid" ,  450 ),
                                         new Student(6 , 27 , "Paris" ,   650 ));

您似乎想要比较列表A中的记录,看看列表B中是否有类似的记录。只有在没有类似匹配的情况下,您才想将该记录添加到结果列表中。如果存在类似的匹配,但几乎完全相同,则需要添加一个新对象。如果存在相同的匹配项,则不会将任何匹配项添加到结果列表中。它就像一个列表。拧动一下removeAll()。

一个常规的双循环是完美的。逐个检查列表A,看看是否可以在列表B中找到类似的记录。找到一条后,检查它是否几乎相同或完全相同。如果快到了,则向结果列表中添加一个新对象。无论哪种方式,都可以在那里停止内部循环。只有当您有一个到达内部循环末尾的记录,而没有找到类似的记录时,才能将该记录添加到结果列表中。可以使用一个标志来检查是否使用布尔值。

    List<Student> listStudentDiffLoops = new ArrayList<>();

    for (Student studentA : listStudentA) {
      boolean found = false;

      for (Student studentB : listStudentB) {
        if (studentA.no == studentB.no &&
            studentA.age == studentB.age &&
            studentA.city.equals(studentB.city)) {
          found = true; // either add a diffsalary Student or none at all

          // if this studentA is different from the found studentB
          if (studentA.salary != studentB.salary) {
            listStudentDiffLoops
              .add(new Student(studentA.no,
                               studentA.age,
                               studentA.city,
                               -Math.abs(studentA.salary - studentB.salary)));
          }
          break; // We're done comparing this studentA
        }
      }

      // there was no similar studentB
      if (!found) listStudentDiffLoops.add(studentA);
    }
    System.out.println("listStudentDiffLoops = " + listStudentDiffLoops);

这将打印:

listStudentDiffLoops = [(1, 20, Rome, -400), (2, 21, Dublin, 250), (5, 27, London, 650)]

我决定使用-Math.abs使工资差异始终为负,这样对象就不会与实际的学生混淆。或者,一个人可以为这些对象创建另一个类:

class StudentSalaryDiff extends Student {
  StudentSalaryDiff(int no, int age, String city, int salary){
    super(no, age, city, salary);
  }
};

对于streams解决方案,您可以将列表a中的每个记录映射到以下任一项:

  • 对于整个列表B,相同的元素ifnoneMatch()返回true

映射对象后,只需过滤掉空对象

请注意,在最坏的情况下,这将为列表A中的每个元素流式传输列表B两次。它将始终流式传输列表B以检查noneMatch(),如果为false,它将再次流式传输列表B:

    List<Student> listStudentDiff =
      listStudentA
        .stream()
        .map(studentA -> {
               if (listStudentB
                     .stream()
                     .noneMatch(studentB -> // noneMatch returns true or false
                                  (studentA.no == studentB.no &&
                                   studentA.age == studentB.age &&
                                   studentA.city.equals(studentB.city)))) {
                 return studentA;         // The same element
               } else { // there is a similar element
                 int otherSalary =
                       listStudentB
                         .stream()
                         .filter(studentB ->
                                   (studentA.no == studentB.no &&
                                    studentA.age == studentB.age &&
                                    studentA.city.equals(studentB.city)))
                         .findFirst()
                         .get() // should not fail since there IS a similar element
                         .salary;
                 if (otherSalary == studentA.salary) {
                   return (Student) null; // null
                 } else {                 
                   return new Student(    // a new object
                                studentA.no,
                                studentA.age,
                                studentA.city,
                                -Math.abs(studentA.salary - otherSalary));
                 }
               }
            })
        .filter(student -> student != null) // remove null objects.
        .collect(Collectors.toList());

如果您喜欢只流式传输列表B一次,您可以使用findFirst()findAny(),这将仅为您提供一个可能为空也可能不为空的结果。结果在可选中给出,您可以在其中使用isPresent()检查是否匹配。

这有点像谢尔盖·阿菲诺热诺夫所接受的答案

解决方案仍然使用map()来返回元素,就像之前的解决方案一样:

    List<Student> listStudentDiffOptional =
      listStudentA
        .stream()
        .map(studentA -> {
               Optional<Student> similarStudent = 
                 listStudentB
                   .stream()
                   .filter(studentB ->
                             (studentA.no == studentB.no &&
                              studentA.age == studentB.age &&
                              studentA.city.equals(studentB.city)))
                   .findFirst();
               if (similarStudent.isPresent()){
                 int salarydiff = similarStudent.get().salary;
                 if (salarydiff == studentA.salary) {
                    return (Student) null; // the two objects are identical
                  } else {
                    return new Student(studentA.no,
                                       studentA.age,
                                       studentA.city,
                                       -Math.abs(studentA.salary - salarydiff));
                  }
               } else {  // there wasn't a similar object
                 return studentA;
               }
            })
        .filter(student -> student != null)
        .collect(Collectors.toList());

所有代码一气呵成:

import java.util.List;
import java.util.ArrayList;
import java.util.stream.Collectors;
import java.util.Optional;

public class Test {
  public static void main(String[] args){

    // --------------------------
    // -------- simple Integers -
    List<Integer> listA = List.of(Integer.valueOf(1),
                                  Integer.valueOf(2),
                                  Integer.valueOf(3),
                                  Integer.valueOf(4),
                                  Integer.valueOf(5));

    List<Integer> listB = List.of(Integer.valueOf(1),
                                  Integer.valueOf(3),
                                  Integer.valueOf(4));

    List<Integer> listResult = 
             new ArrayList<>(listA);  // creating an identical list
    listResult.removeAll(listB);      // subtract the elements present in list B

    System.out.println("listA: " + listA);
    System.out.println("listB: " + listB);
    System.out.println("listA \"minus\" listB: " + listResult);
    System.out.println();

    List<Integer> listResultStream = 
      listA.stream()
//           .filter(intA -> 
//                     !(listB.stream()
//                            .anyMatch(intB -> intB.equals(intA))))
           .filter(intA -> 
                     listB.stream()
                          .noneMatch(intB -> intB.equals(intA)))
           .collect(Collectors.toList());


    System.out.println("listA \"minus\" listB using listResultStream: " + listResultStream);
    System.out.println();

    // ---------------------------
    // -------- complex Students -
/*
    List<Student> listStudentA = List.of(new Student(1 , 20 , "Rome" ,    2001, 300 ),
                                         new Student(2 , 21 , "Dublin" ,  2005, 250 ),
                                         new Student(3 , 24 , "Istanbul", 2012, 350 ),
                                         new Student(4 , 27 , "Madrid" ,  2002, 450 ),
                                         new Student(5 , 27 , "London" ,  2005, 650 ),
                                         new Student(6 , 27 , "Paris" ,   2005, 650 ));
   
    List<Student> listStudentB = List.of(new Student(1 , 20 , "Rome" ,    2014, 700 ),
                                         new Student(3 , 24 , "Istanbul", 2012, 350 ),
                                         new Student(4 , 27 , "Madrid" ,  2009, 450 ),
                                         new Student(6 , 27 , "Paris" ,   2005, 650 ));
*/

    List<Student> listStudentA = List.of(new Student(1 , 20 , "Rome" ,    300 ),
                                         new Student(2 , 21 , "Dublin" ,  250 ),
                                         new Student(3 , 24 , "Istanbul", 350 ),
                                         new Student(4 , 27 , "Madrid" ,  450 ),
                                         new Student(5 , 27 , "London" ,  650 ),
                                         new Student(6 , 27 , "Paris" ,   650 ));
   
    List<Student> listStudentB = List.of(new Student(1 , 20 , "Rome" ,    700 ),
                                         new Student(3 , 24 , "Istanbul", 350 ),
                                         new Student(4 , 27 , "Madrid" ,  450 ),
                                         new Student(6 , 27 , "Paris" ,   650 ));

    List<Student> listStudentDiffLoops = new ArrayList<>();

    // --------------------------
    // -------- for loops -------
    for (Student studentA : listStudentA) {
      boolean found = false;

      for (Student studentB : listStudentB) {
        if (studentA.no == studentB.no &&
            studentA.age == studentB.age &&
            studentA.city.equals(studentB.city)) {
          found = true; // either add a diffsalary Student or none at all

          // if this studentA is different from the found studentB
          if (studentA.salary != studentB.salary) {
            listStudentDiffLoops
              .add(new Student(studentA.no,
                               studentA.age,
                               studentA.city,
                               -Math.abs(studentA.salary - studentB.salary)));
          }
          break; // We're done comparing this studentA
        }
      }

      // there was no similar studentB
      if (!found) listStudentDiffLoops.add(studentA);
    }
    System.out.println("listStudentDiffLoops = " + listStudentDiffLoops);

    // --------------------------
    // -------- streaming twice -
    List<Student> listStudentDiff =
      listStudentA
        .stream()
        .map(studentA -> {
               if (listStudentB
                     .stream()
                     .noneMatch(studentB ->
                                  (studentA.no == studentB.no &&
                                   studentA.age == studentB.age &&
                                   studentA.city.equals(studentB.city)))) {
                 return studentA;         // The same element
               } else { // there is a similar element
                 int otherSalary =
                       listStudentB
                         .stream()
                         .filter(studentB -> // noneMatch returns true or false
                                   (studentA.no == studentB.no &&
                                    studentA.age == studentB.age &&
                                    studentA.city.equals(studentB.city)))
                         .findFirst()
                         .get() // should not fail since there IS a similar element
                         .salary;
                 if (otherSalary == studentA.salary) {
                   return (Student) null; // null
                 } else {                 
                   return new Student(    // a new object
                                studentA.no,
                                studentA.age,
                                studentA.city,
                                -Math.abs(studentA.salary - otherSalary));
                 }
               }
            })
        .filter(student -> student != null) // remove null objects.
        .collect(Collectors.toList());
    System.out.println("listStudentDiff: " + listStudentDiff);

    // --------------------------
    // -------- using optional --
    List<Student> listStudentDiffOptional =
      listStudentA
        .stream()
        .map(studentA -> {
               Optional<Student> similarStudent = 
                 listStudentB
                   .stream()
                   .filter(studentB ->
                             (studentA.no == studentB.no &&
                              studentA.age == studentB.age &&
                              studentA.city.equals(studentB.city)))
                   .findFirst();
               if (similarStudent.isPresent()){
                 int salarydiff = similarStudent.get().salary;
                 if (salarydiff == studentA.salary) {
                    return (Student) null; // the two objects are identical
                  } else {
                    return new Student(studentA.no,
                                       studentA.age,
                                       studentA.city,
                                       -Math.abs(studentA.salary - salarydiff));
                  }
               } else {  // there wasn't a similar object
                 return studentA;
               }
            })
        .filter(student -> student != null)
        .collect(Collectors.toList());
    System.out.println("listStudentDiffOptional: " + listStudentDiffOptional);
  }
}


// ------------------------------
// ------------------------------
class Student {
  int       no;
  int       age;
  String    city;
//  LocalDate birth_date;
  int       salary;

//  Student(int no, int age, String city, int birth_date, int salary){
  Student(int no, int age, String city, int salary){
    this.no = no;
    this.age = age;
    this.city = city;
//    this.birth_date = LocalDate.of(birth_date, 1, 1); // January 1st.
    this.salary = salary;
  }

  @Override
  public String toString(){
    return "(" +
           no + ", " +
           age + ", " + 
           city + ", " +
//           birth_date + ", " +
           salary +
           ")";
  }
}

class StudentSalaryDiff extends Student {
  StudentSalaryDiff(int no, int age, String city, int salary){
    super(no, age, city, salary);
  }
};

幸阳波
2023-03-14

我使用了自定义收集器对象,看起来不是很好,但似乎可以工作:

List<Student> listDiffList =
  A.stream()
   .collect(ArrayList::new,
           (a, two) -> {
             Optional<Student> one = B.stream()
                                      .filter(s -> s.getNo().equals(two.getNo())
                                                   && s.getAge().equals(two.getAge())
                                                   && s.getCity().equals(two.getCity()))
                                      .findAny();


             if (one.isPresent()) {
                 if (!one.get().getSalary().equals(two.getSalary())) {
                     two.setSalary(two.getSalary()-one.get().getSalary());
                     a.add(two);
                 }
             }
             else a.add(two);
           },
           ArrayList::addAll);
 类似资料:
  • 我试图理解Java8流。我有两门课: 以及: 字段应指示全天的卡路里总量是否为。该字段与当天的所有条目相同。 我试图从

  • 问题内容: 我有以下Java6和Java8代码: Java8中有什么方法可以使用Lambda以更简洁的方式处理前一个问题? 问题答案: 流绑定到给定的可迭代/集合,因此您不能真正地“并行”迭代两个集合。 一种解决方法是创建索引流,但不一定比for循环有所改进。流版本可能如下所示:

  • 我有一个要求,我需要根据2个条件从另一个列表中创建一个对象列表。对象应处于会话中或处于活动状态。[这个问题与使用java8流从另一个对象创建对象列表有着模糊的联系 主要类别: 任何帮助都将不胜感激。

  • 问题内容: 我有看起来像的数据框: 为了进一步处理数据,我需要拆分该列,然后将其替换为如下所示的多列: 因此,这些列可以追加到初始数据帧。我不知道该怎么做,因为像 不能解决我的问题,因为我不仅需要基于列表中位置的列,还需要基于列表中每个唯一值的列。您知道我该如何处理吗? 问题答案: 您可以使用和: 如果需要计数值,则可以使用(我添加一个字符串进行测试):

  • 我有两个对象列表,它们在两个列表中都有重复名称。我需要从清单2中删除清单1中的所有重复值。 下面是一个场景,类有名称变量,用这个变量需要检查清单1中的重复值并需要删除。 //这是具有3个对象的第一个列表 清单1大小为1 请建议我在Java8与流。