skidalgo
skidalgo

Reputation: 19

Filter list by properties of a field of the list object

Given all TypeA instances in a List<TypeA> have an instance of SubTypeB set in the superTypeB field, I need to filter out duplicate TypeA instances from the list, where a duplicate means the values of propertyA and propertyB from the SubTypeB both match. Is there a way to do this with the Java 8 stream API?

public class TypeA {
  private SuperTypeB superTypeB;
  public SuperTypeB getSuperTypeB(){ return superTypeB; }
}

public class SuperTypeB {
  private String propertyX;
}

public class SubTypeB extends SuperTypeB {
  private String propertyA;
  private String propertyB;
  public String getPropertyA(){ return propertyA; }
  public String getPropertyB(){ return propertyB; }
}

Upvotes: 1

Views: 95

Answers (2)

Naman
Naman

Reputation: 31868

You need to be sure to not map before you filter to collect to the same type. Using a distinctByKey utility, you can further choose to collect to a List as :

List<TypeA> filteredTypeAs = typeAList.stream()
        .filter(distinctByKey(s -> {
            SubTypeB subTypeB = (SubTypeB) s.getSuperTypeB();
            return Arrays.asList(subTypeB.getPropertyA(), subTypeB.getPropertyB());
        }))
        .collect(Collectors.toList());

Note: This relies on the assumption as stated in the question that all the subtypes are possible to cast without an instanceof check.

Upvotes: 1

Rajan Prasad
Rajan Prasad

Reputation: 1679

Is it possible for you to override equals and hashCode method for the types TypeA and SubTypeB?

If yes, this is fairly straightforward. If no, I will edit this answer accordingly.

class TypeA {
    private SuperTypeB superTypeB;
    public SuperTypeB getSuperTypeB(){ return superTypeB; }


        @Override
        public boolean equals( final Object o) {
            if (this == o) return true;
            if (!(o instanceof TypeA)) return false;
            TypeA typeA = (TypeA) o;
            return superTypeB.equals(typeA.superTypeB);
        }

        @Override
        public int hashCode() {
            return Objects.hash(superTypeB);
        }
    }

    class SubTypeB extends SuperTypeB {
        private String propertyA;
        private String propertyB;
        public String getPropertyA(){ return propertyA; }
        public String getPropertyB(){ return propertyB; }


        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (!(o instanceof SubTypeB)) return false;
            SubTypeB subTypeB = (SubTypeB) o;
            return propertyA.equals(subTypeB.propertyA) &&
                propertyB.equals(subTypeB.propertyB);
        }

        @Override
        public int hashCode() {
            return Objects.hash(propertyA, propertyB);
        }

    }

After this, all you need to do is this:

List<TypeA> finalList = list.stream().distinct().collect(Collectors.toList());

The only assurance that you can get in the stream API that you will get the first occurrence of each distinct object is through the order in which the stream emits objects. Otherwise, you have no control over which element you encounter first.

Upvotes: 0

Related Questions