Search code examples
javajava-streamintersection

How to obtain an Intersection of a List of complex custom Objects and a List of its String attributes and in Java using Stream API


Here's my domain classes Location and Address:

public class Location {
    GPS gps;
    Address address;
    // etc.
}

public class Address {
    String street;
    String number;
    // etc.
}

Let's say I have a List of String's

List<String> houseNumbers

and a List of Locations

List<Location> locations

Now, I want to have only those Locations where the Location.address.number matches with any of the Strings in the houseNumbers list.

I've the following, which produces a compilation error: "expecting a Predicate <? super java.lang.String> instead of a String".

My attempt:

List<Location> filteredLocations = locations.stream()
    .anyMatch(location -> 
        housenumbers.stream().anyMatch(location.address.number)
    );

But how do I make sure to compare to every item in the houseNumbers List?

The following snippet did the trick:

List<Location> filteredLocations = 
 locations.stream().filter(
   location -> housenumbers.contains(location.address.number)
).collect(Collectors.toList());

Solution

  • Dump the data from the houseNumbers into a HashSet a then in the stream check the house number of each Location against the Set.

    For that, you would need to apply:

    • filter() operation to discard the locations, which has a house number that is not present in the list;
    • toList() as a terminal operation to collect the result into a list (or collect() with Collectors.toList() specified as an argument if you're using JDK 15 or earlier).
    List<String> houseNumbers = // initializing the list
    Set<String> numbers = new HashSet<>(houseNumbers);
    
    List<Location> locations = // initializing the list
        
    List<Location> filteredLocations = locations.stream()
        .filter(location -> numbers.contains(location.getAddress().getNumber()))
        .toList(); // for Java 16+ or collect(Collectors.toList())
    

    *Sidenote: in Java, we use access modifiers to encapsulate the data properly withing the class. And getter methods serve a mean of accessing the data. Don't keep the fields public (or package-private). If you're doing a lot of JavaScript you might be accustomed to this location.address.number, but in Java it's not appropriate.