2

I want to compare 2 java lists, which contain 2 different types of objects. However, both types of objects have some common properties, and I want to compare on them.

class A {
  int id;
} 

class B {
  int id2;
}

List<A> listA; // contains some objects
List<B> listB

// comparison logic
boolean isEqual = true;
if(listA.size() != listB.size())
  isEqual = false;

for(int i=0;i<listA.size();++i) {
  if(!listA.get(i).equals(listB.get(i)) {
    isEqual = false;
    break;
  }
}

Is there a good way to write this concisely via java streams?

4
  • Unlikely (but i can be wrong). But I would use Iterator instead of get(int) method. Commented Oct 12, 2018 at 7:16
  • How could the comparison logic possibly work? Is a.equals(b) true when a.id=b.id2? Commented Oct 12, 2018 at 10:44
  • @Michal, you can assume its a validation check instead of equality, if that makes more sense Commented Oct 12, 2018 at 16:42
  • My point is that both the question and accepted answer do equals on instance of A getting instance of B as parameter, I guess that shall be a.id and b.id2. Commented Oct 15, 2018 at 8:04

6 Answers 6

2

One way to do this would be using IntStream:

boolean equal = (IntStream.range(0, listA.size())
     .filter(i -> listA.get(i).equals(listB.get(i))).count() == listA.size());

This won't work for list of different sizes but that is just a simple condition (listA.size() == listB.size()) I omitted for brevity.

EDIT:

It can be improved with noneMatch():

boolean equal = IntStream.range(0, listA.size())
                         .noneMatch(i -> listA.get(i).id != listB.get(i).id2);
Sign up to request clarification or add additional context in comments.

7 Comments

This is just a toy example. In the actual use case, I want to compare 3 properties for class A and B objects to decide if the validation succeeds.
Will this solution throw IndexOutOfBoundsException, if listB.size() < listA.size()?
Would it not fail if listB has less no of elements than listA?
If the first elements are not equals, you still check the rest. I would use an anyMatch with the reversed condition
Thanks, I misunderstood earlier. This will work as expected. However, I'm not so sure now that this looks better than earlier approach.
|
0

Easiest way to be done so you can actually check and control all the vars you want to compare is something like the following. Just keep the patern and add more ifs where you want to make the .equals() checks and also it will work with any size of the lists and you wont get an IndexOutOfBounds Exception

   class A {
      int id;
    } 

    class B {
      int id2;
    }

    List<A> listA; // contains some objects
    List<B> listB;

    // comparison logic
    boolean isEqual = true;
        for(int i=0;i<listA.size();++i) {
            A objectA = listA.get(i);
            for(int y=0; y<listB.size();y++){
              B objectB = listB.get(y);

              if(!objectA.getVar().equals(objectB.getVar())) { 
                //And so on for every same object the two classes have and can be compared.
                isEqual = false;
                break;
              }
           }
        }
    }

Comments

0

I think you should introduce some service class with method which consumes and A and B as objects arguments and compare those objects and return boolean. This approach allow you to compare two diffrent objects by properties. Something like that:

public class CompareService {

    public boolean comparteObjectAToB(ObjectA a, ObjectB b) {
        //logic
    }
}

public class SomeService {

    private CompareService compareService = new CompareService();

    public boolean compare() { 
        List<ObjectA> listA = new ArrayList<>();
        List<ObjectB> list2 = new ArrayList<>();
        //some logic which fill lists, empty checks etc

        return listA.stream().anyMatch(o1 -> list2.stream().anyMatch(o2 -> compareService.comparteObjectAToB(o1, o2)));
        }
    }

Comments

0

With your code, it possible to throw if IndexOutOfBoundsException if A more than B.

For me, I will do this.

//Change id in class A and B from int to Integer

Boolean isEqual = Boolean.True;
Iterator<A> iterA = listA.iterator();
Iterator<B> iterB = listB.iterator();

while( iterA.hasNext() && iterB.hasNext()){
     A a = iterA.next();
     B b = iterB.next();

     if( !a.getA().equals(b.getB()){
          isEqual = Boolean.False;
          return;
     }
}

Comments

0

First make an interface, containing common properties. Then add implements NewInterface to class A and class B. Then implement comparator, comparing those interfaces. And then use that comparator to compare them.

Comments

0

Given two bean classes, e.g.

@Immutable
static class Person {
    private final String firstName;
    private final String secondName;
    private final String phoneNumber;

    public Person(String firstName, String secondName, String phoneNumber) {
        this.firstName = firstName;
        this.secondName = secondName;
        this.phoneNumber = phoneNumber;
    }
    public String getFirstName() {
        return this.firstName;
    }
    public String getSecondName() {
        return this.secondName;
    }
    public String getPhoneNumber() {
        return this.phoneNumber;
    }
}

@Immutable
static class Company {
    private final String name;
    private final String phoneNumber;
    public Company(String name, String phoneNumber) {
        this.name = name;
        this.phoneNumber = phoneNumber;
    }
    public String getName() {
       return this.name;
    }
    public String getPhoneNumber() {
       return this.phoneNumber;
    }
}

the common properties could be placed in third class, e.g.

static class CommonData {
    private final String phoneNumber;

    public CommonData(String phoneNumber) {
        this.phoneNumber = phoneNumber;
    }

    public String getPhoneNumber() {
        return this.phoneNumber;
    }   

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof ListComparisTest.CommonData) {
            ListComparisTest.CommonData rhs = (ListComparisTest.CommonData) obj;
            return new EqualsBuilder()
                .append(this.phoneNumber, rhs.phoneNumber)
                .isEquals();
        }
        return false;
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder()
            .append(phoneNumber)
            .toHashCode();
    }

}

and two lists compared like this

@Test
public  void testCompareLists() {
    Function<Person, CommonData> person2CommonData = p->new CommonData(p.getPhoneNumber());
    Function<Company, CommonData> company2CommonData = c->new CommonData(c.getPhoneNumber());

    List<Person> persons = Arrays.asList(new Person("James", "Cook", "1234566" ), new Person("Francis", "Drake", "2345667" )); 
    List<Company> companys = Arrays.asList(new Company("Google",  "1234566" ), new Company("IBM",  "2345667" )); 

    List<CommonData> personsCommonData = persons.stream().map(person2CommonData).collect(Collectors.toList());
    List<CommonData> companysCommonData = companys.stream().map(company2CommonData).collect(Collectors.toList());

    Assert.assertTrue(personsCommonData.equals(companysCommonData));
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.