-2

I have 2 custom list of Java objects.

@Data
Class A{
  int no;
  int age;
  int foo;
  int bar;
}

List<A> aList = populateFromDB();
List<A> bList = populateFromReq();

Now I want a 3rd list which will contain filtered objects from bList.

The filtering is based on this criteria :

no should match;
age OR foo anyone should differ;

Sample Data set :

Input :
aList = [{2,20,50,60} , {3,25,40,60} , {4,40,10,20}]
bList = [{2,10,50,60} , {3,25,50,60} , {4,40,10,20}]


Output :
filteredList = [{2,10,50,60} , {3,25,50,60}]  
// first 2 elements in bList are different hence they should be added to the filtered list.

// aList contains records from DB.
// bList contains records of same size as aList but few elements are different.
// Need to compare bList with aList and find the records that are different and create a separate list and update those records in DB.

I am trying this code :

List<A> filteredList = bList.stream()
                            .filter(i -> aList.stream()
                                               .anyMatch(j -> (j.getNo() == i.getNo() &&
                                                               (j.getAge() != i.getAge() || 
                                                               j.getFoo() != i.getFoo())
                                                              )
                                                         )
                                   )
                             .collect(Collectors.toList());

But the above is not giving me desired results. It is giving me records which does not fulfil the criteria I have mentioned. I am not sure where I am making wrong.

I have also tried the below code :

List<A> intersect = a.stream()
                    .filter(b::contains)
                    .collect(Collectors.toList());

Can anyone help me how to compare 2 custom list based on some fields and form a new list.

UPDATE-1 : Its not about comparing String. So for simplicity sake I am replacing strings with Integers.

UPDATE-2 : Added sample i/p and o/p data.

Som
  • 1,522
  • 1
  • 15
  • 48

1 Answers1

2

I added a constructor, matches method and a toString() method to your class. The matches method was created since overriding equals would not meet the reflexive requirements where x.equals(x) is true.

class A {

    public A(int no, int age, int foo, int bar) {
        this.no = no;
        this.age = age;
        this.foo = foo;
        this.bar = bar;
    }

    int no;
    int age;
    int foo;
    int bar;

    public boolean matches(A a) {
        return a.no == this.no && (a.age != this.age || a.foo != this.foo);
    }

    @Override
    public String toString() {
        return "A{%d, %d, %d, %d}".formatted(no, age, foo, bar);
    }
}

Now generate some data

List<A> aList = new ArrayList<>();
List<A> bList = new ArrayList<>();
Random r = new Random();
for (int i = 0; i < 10; i++) {
    aList.add(new A(r.nextInt(5), r.nextInt(5), r.nextInt(5),
            r.nextInt(5)));
    bList.add(new A(r.nextInt(5), r.nextInt(5), r.nextInt(5),
            r.nextInt(5)));
}

Now print the generated values.

for (int i = 0; i < aList.size(); i++) {
    System.out.printf("aList(%d) = %s     bList(%d) = %s%n", i, aList.get(i), i, bList.get(i));
}
  • iterate over the aList and check to see if corresponding elements satisfy the match criteria.
  • if so, add a to the results list.
  • then print the results.
List<A> results = new ArrayList<>();
int i = 0;
for (A a : aList) {
    if (a.matches(bList.get(i++))) {
        results.add(a);
    }
}

System.out.println();
results.forEach(System.out::println);

output will be something like the following:

aList(0) = A{0, 1, 4, 1}     bList(0) = A{0, 2, 2, 2}
aList(1) = A{1, 4, 0, 3}     bList(1) = A{1, 2, 2, 3}
aList(2) = A{1, 1, 1, 4}     bList(2) = A{1, 4, 2, 2}
aList(3) = A{0, 4, 3, 3}     bList(3) = A{3, 2, 0, 2}
aList(4) = A{0, 4, 3, 1}     bList(4) = A{1, 0, 4, 3}
aList(5) = A{3, 2, 3, 1}     bList(5) = A{1, 2, 0, 3}
aList(6) = A{4, 3, 0, 2}     bList(6) = A{3, 4, 0, 3}
aList(7) = A{4, 3, 2, 4}     bList(7) = A{2, 1, 2, 2}
aList(8) = A{0, 3, 4, 3}     bList(8) = A{3, 3, 4, 4}
aList(9) = A{1, 3, 3, 4}     bList(9) = A{0, 1, 2, 0}

A{0, 1, 4, 1}
A{1, 4, 0, 3}
A{1, 1, 1, 4}

Updated Answer

  • Here is one way. This won't work unless your lists are declared final or are effectively final.
final List<A> aList = List.of(new A(2, 20, 50, 60), new A(3, 25, 40, 60),
        new A(4, 40, 10, 20));
final List<A> bList = List.of(new A(2, 10, 50, 60), new A(3, 25, 50, 60),
        new A(4, 40, 10, 20));

List<A> results = IntStream.range(0, aList.size())
        .filter(k -> aList.get(k).matches(bList.get(k)))
        .mapToObj(aList::get).toList();

results.forEach(System.out::println);

prints

A{2, 20, 50, 60}
A{3, 25, 40, 60}

WJS
  • 36,363
  • 4
  • 24
  • 39
  • Hi Can u please check my sample data and let me know how we can achieve that using java 8 streams. I don't want actually the brute force way to do that. In ur sample I can see all elements in aList and bList differing. Test with my sample data once. – Som Feb 07 '23 at 06:55
  • @Som I updated the answer to include a stream solution with your data. – WJS Feb 07 '23 at 13:00