Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java filter List that so it only contains objects that have same attribute as in another lists

I got 2 lists containing several objects. I want to filter the objects that contain the same String value at a specific attribute. So let's say listA contains objects with attribute id. Same for listB, although it contains different objects. Some objects from both lists have the same id though. I want to filter these objects and put them in the a new list. This is what i got so far:

List<Customer> Clist = Customer.getAllCustomers();
    List<License> Llist = License.getAllLicenses();

    Predicate<Customer> customerNotNullPredicate = u -> (u.id != null);
    Predicate<License> licenseNotNullPredicate = u -> (u.id != null);

    List<Customer> Clistfiltered1 = Clist.parallelStream().filter(customerNotNullPredicate).collect(Collectors.toList());
    List<License> Llistfiltered1 = Llist.parallelStream().filter(licenseNotNullPredicate).collect(Collectors.toList());
    Clistfiltered1.retainAll(Llistfiltered1);
    try {
        Clistfiltered1.get(0);
    } catch (Exception e){
        System.out.println(e);
    }

If course, retainAll() doesn't return anything, as both lists just contain objects of the different type. How can i try to use retainAll() on a specific attribute of the objects?

Thank you a lot in advance.

like image 342
Elias Johannes Avatar asked Jun 09 '17 10:06

Elias Johannes


2 Answers

Your task description is not clear, but apparently, you want to get all Customer instance, for which a License instance having the same id exist.

While it is possible to describe this as one stream operation, searching for a match in one list for every element of the other list would imply an operation of O(nxm) time complexity, in other words, it would perform very bad if you have large lists.

Therefore, it’s better to do it in two operations having O(n+m) time complexity:

List<Customer> cList = Customer.getAllCustomers();
List<License> lList  = License.getAllLicenses();

Set<?> licenseIDs = lList.stream()
    .map(l -> l.id).filter(Objects::nonNull)
    .collect(Collectors.toSet());

List<Customer> cListFiltered = cList.stream()
    .filter(c -> licenseIDs.contains(c.id))
    .collect(Collectors.toList());

if(cListFiltered.isEmpty()) System.out.println("no matches");
else cListFiltered.forEach(System.out::println);

While the exact Set type returned by collect(Collectors.toSet()) is unspecified, you can expect it to have a better than linear lookup, which allows to use its contains method in the subsequent stream operation. Note that only the first operation has a filter for null values; since that guarantees that the licenseIDs set does not contain null, customers with a null id are rejected implicitly.

It’s easy to get the common IDs instead

Set<?> commonIDs = cList.stream()
    .map(l -> l.id).filter(licenseIDs::contains)
    .collect(Collectors.toSet());

Using commonIDs, you may filter both, the lists of customers or the list of licenses, if you wish.

like image 90
Holger Avatar answered Sep 23 '22 00:09

Holger


in this case you can't use retainAll() , but I would solve the problem like this:

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


public class DifferentCollections {

    public static void main(String[] args) {


        List<Customer> customers = new ArrayList<>(Arrays.asList(new Customer(1), new Customer(2), new Customer(10)));
        List<License> licenses = new ArrayList<>(Arrays.asList(new License(1), new License(2), new License(30)));

        List<Customer> filteredCustomers = customers.stream().
                filter(c -> customerIdFoundInLicensesList(c, licenses)).
                collect(Collectors.toList());

        System.out.println(filteredCustomers);
    }

    private static boolean customerIdFoundInLicensesList(Customer customer, List<License> licenses) {
        return licenses.stream().
                filter(l -> l.getId().equals(customer.getId())).
                findAny().
                isPresent();
    }
}

class Customer {
    Integer id;

    public Customer(Integer id) {
        this.id = id;
    }

    public Integer getId() {
        return id;
    }

    @Override
    public String toString() {
        return "Customer{" + "id=" + id + '}';
    }
}

class License {
    Integer id;

    public License(Integer id) {
        this.id = id;
    }

    public Integer getId() {
        return id;
    }

    @Override
    public String toString() {
        return "License{" + "id=" + id + '}';
    }
}
like image 39
Jose Zevallos Avatar answered Sep 26 '22 00:09

Jose Zevallos