Reputation: 1524
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.
Upvotes: 69
Views: 175808
Reputation: 1758
Try this:
SortedMap<String, Account> accountMap, List<AccountReseponse> accountOwnersList
List<Map.Entry<String, Account>> entryList = accountMap.entrySet().stream().filter(account -> accountOwnersList.stream()
.anyMatch(accountOwner -> accountOwner.getAccount()
.getIdentifier().equals(account.getValue().getIdentifier())))
.collect(Collectors.toList());
Can also use .noneMatch().
Upvotes: 1
Reputation: 8219
@Alexis'a answer is nice, but I have another way around to get use of performance from Map
and improve the part you do listCarName.stream().map(DataCarName::getName).anyMatch(name -> name.equals(e.getName()))
for each item, first I make a map from listCar
and making the key with the field that I want to compare, in this instance is car's name
and filter out null values when I map the list1 to be CarData
.
So it should be something like:
final Map<String, CarData> allCarsMap = listCar // Your List2
.stream().collect(Collectors.toMap(CarData::getName, o -> o));
final List<CarData> listOutput = // Your expected result
listCarName // Your List1
.stream()
.map(allCarsMap::get) // will map each name with a value in the map
.filter(Objects::nonNull) // filter any null value for any car name that does not exist in the map
.collect(Collectors.toList());
I hope this helps, maybe a little better performance in some scenarios?
Upvotes: 0
Reputation: 6491
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());
Upvotes: 13
Reputation: 93902
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());
Upvotes: 118