Java 基于另一个列表的 1 个列表的流过滤器

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/36246998/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 17:37:02  来源:igfitidea点击:

Stream Filter of 1 list based on another list

javaarraylistjava-8java-stream

提问by iCoder

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

我在此论坛和谷歌中搜索后发布了我的查询,但无法解决相同的问题。例如:Link1 Link2 Link3

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

我正在尝试根据列表 1 中的值过滤列表 2(多列)。

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.

在上面的代码中,如果我提供了可以过滤的特定值,但不确定如何检查列表 2 中的汽车名称是否存在于列表 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).

希望我面临的问题很清楚,等待指导(我对 Java 还是比较新的,因此如果上述查询非常基本,请原谅)。

EditI 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.

编辑我相信上面提供的 link-3 应该可以解决,但在我的情况下它不起作用。也许是因为 list-1 中的值填充为 org.gradle04.Main.Cars.dataCarName@4148db48 .. 等等。只有当我通过调用 getName 在 List 1 上执行 forEach 时,我才能以人类可读的格式获取值方法。

采纳答案by Alexis C.

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

目前尚不清楚为什么您将 aList<DataCarName>放在首位而不是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 -> e.getName().contains("BMW")只会检查数据车的名称是否包含不是您想要的 BMW。你的第一次尝试可能是

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

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

但是由于listCarName是一个List<DataCarName>e.getName()一个字符串(我认为),因此您将得到一个空列表。

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 containsas a predicate on this set:

现在这是非常昂贵的,因为您为数据汽车流管道中的每个实例创建一个流。更好的方法是Set<String>预先使用汽车的名称构建一个,然后简单地contains用作该集合的谓词:

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());

回答by Hank D

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:

在您的 DataCar 类型中,是否getName()返回 String 或 DataCarName 枚举类型?如果是枚举,您可能会遵循 Alexis C 的方法,但不是使用 Collectors.toSet() 构建 HashSet,而是构建一个 EnumSet,它提供 O(1) 性能。修改 Alexis 的建议,结果如下:

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());