Stream Filter of 1 list based on another list

109,273

Solution 1

It's not clear why you have a List<DataCarName> in first place instead of a List/Set<String>.

The predicate you have to provide must check if for the corresponding data car instance, there's its name in the list.

e -> e.getName().contains("BMW") will only check if the name of the data car contains BMW which is not what you want. Your first attempt then may be

e -> listCarName.contains(e.getName())

but since listCarName is a List<DataCarName> and e.getName() a string (I presume), you'll get an empty list as a result.

The first option you have is to change the predicate so that you get a stream from the list of data car names, map them to their string representation and check that any of these names corresponds to the current data car instance's name you are currently filtering:

List<DataCar> listOutput =
    listCar.stream()
           .filter(e -> listCarName.stream().map(DataCarName::getName).anyMatch(name -> name.equals(e.getName())))
           .collect(Collectors.toList());

Now this is very expensive because you create a stream for each instance in the data car stream pipeline. A better way would be to build a Set<String> with the cars' name upfront and then simply use contains as a predicate on this set:

Set<String> carNames = 
    listCarName.stream()
               .map(DataCarName::getName)
               .collect(Collectors.toSet());

List<DataCar> listOutput =
     listCar.stream()
            .filter(e -> carNames.contains(e.getName()))
            .collect(Collectors.toList());

Solution 2

in your DataCar type, does getName() return a String or the DataCarName enum type? If it is the enum, you might follow Alexis C's approach but instead of building a HashSet using Collectors.toSet(), build an EnumSet, which gives O(1) performance. Modifying Alexis' suggestion, the result would look like:

Set<DataCarName> carNames = 
    listCarName.stream()
               .collect(Collectors.toCollection(
                   ()-> EnumSet.noneOf(DataCarName.class)));

List<DataCar> listOutput =
    listCar.stream()
               .filter(car -> carNames.contains(car.getName()))  
               .collect(Collectors.toList());
Share:
109,273
iCoder
Author by

iCoder

I am a tech enthusiast with a new found love to learn Java. At present am learning &amp; writing program in Java. In the past, I was a SAP Certified Functional consultant &amp; worked on Implementations projects across the globe.

Updated on September 29, 2021

Comments

  • iCoder
    iCoder over 2 years

    I am posting my query after having searched in this forum & google, but was unable to resolve the same. eg: Link1 Link2 Link3

    I am trying to filter List 2 (multi column) based on the values in List 1.

    List1:
     - [Datsun]
     - [Volvo]
     - [BMW]
     - [Mercedes]
    
    List2: 
     - [1-Jun-1995, Audi, 25.3, 500.4, 300]
     - [7-Apr-1996, BMW, 35.3, 250.2, 500]
     - [3-May-1996, Porsche, 45.3, 750.8, 200]
     - [2-Nov-1998, Volvo, 75.3, 150.2, 100]
     - [7-Dec-1999, BMW, 95.3, 850.2, 900]
    
    expected o/p:
     - [7-Apr-1996, BMW, 35.3, 250.2, 500]
     - [2-Nov-1998, Volvo, 75.3, 150.2, 100]
     - [7-Dec-1999, BMW, 95.3, 850.2, 900]
    

    Code

    // List 1 in above eg
    List<dataCarName> listCarName = new ArrayList<>(); 
    // List 2 in above eg
    List<dataCar> listCar = new ArrayList<>(); 
    
    // Values to the 2 lists are populated from excel
    
    List<dataCar> listOutput = listCar.stream().filter(e -> e.getName().contains("BMW")).collect(Collectors.toList());
    

    In the above code if I provide a specific value I can filter, but not sure how to check if Car Name in List 2 exits in List 1.

    Hope the issue I face is clear, await guidance (Am still relatively new to Java, hence forgive if the above query is very basic).

    Edit I believe the link-3 provided above should resolve, but in my case it is not working. Maybe because the values in list-1 are populated as org.gradle04.Main.Cars.dataCarName@4148db48 .. etc. I am able to get the value in human readable format only when I do a forEach on List 1 by calling the getName method.

  • iCoder
    iCoder about 8 years
    thanks for your time & a sincere appreciation for your guidance.
  • iCoder
    iCoder about 8 years
    Thanks for your inputs, will check at my end to see if your suggestion makes a difference for my specific need.