基于另一组的Java 8过滤器集

时间:2016-01-11 18:40:06

标签: java java-8

使用Java 8新构造,例如流,有没有办法根据另一个集合中的顺序过滤Set

Set<Person> persons = new HashSet<>();

persons.add(new Person("A", 23));
persons.add(new Person("B", 27));
persons.add(new Person("C", 20));

List<String> names = new ArrayList<>();
names.add("B");
names.add("A");

我希望根据persons过滤来自names集的项目,这样只会保留names中指定其姓名的人,但会按照他们出现的顺序进行保留在names

所以,我想要

Set<Person> filteredPersons = ...;

其中第一个元素是Person("B", 27),第二个元素是Person("A", 23)

如果我执行以下操作,

Set<Person> filteredPersons = new HashSet<>(persons);
filteredPersons = filteredPersons.stream().filter(p -> names.contains(p.getName())).collect(Collectors.toSet());

如果我没有记错的话,订单不能保证与names相同。

我知道如何使用简单的 for 循环实现此目的;我只是在寻找java 8的方法。

感谢您的期待!

修改

for 循环实现了相同的结果:

Set<Person> filteredPersons = new LinkedHashSet<>();
for (String name : names) {
  for (Person person : persons) {
    if (person.getName().equalsIgnoreCase(name)) {
      filteredPersons.add(person);
      break;
    }
  }
}

LinkedHashSet实施确保维持订单。

3 个答案:

答案 0 :(得分:6)

final Set<Person> persons = ...
Set<Person> filteredPersons = names.stream()
    .flatMap(n -> 
        persons.stream().filter(p -> n.equals(p.getName()))
    )
    .collect(Collectors.toCollection(LinkedHashSet::new));

收集通过每个名称过滤创建的人员流。对于像提供的示例这样的情况,这很快,但会随着人数线性增加,如O(N * P)。

对于较大的人员和姓名集合,创建可用于按姓名查找人员的索引总体上会更快,缩放为O(N + P):

Map<String, Person> index = persons.stream()
    .collect(Collectors.toMap(Person::getName, Function.identity()));
Set<Person> filteredPersons = names.stream()
    .map(index::get)
    .filter(Objects::nonNull)
    .collect(Collectors.toCollection(LinkedHashSet::new));

答案 1 :(得分:3)

  

我愿意更改设置实现,例如更改为   LinkedHashSet如上例所示,以实现最终目标。

如果您完全愿意更改用于存储persons的数据结构,那么您应该考虑使用Map,因为它会大大提高算法的效率。

Map<String, Person> persons = new HashMap<>();

persons.put("A", new Person("A", 23));
persons.put("B", new Person("B", 27));
persons.put("C", new Person("C", 20));

List<String> names = new ArrayList<>();
names.add("B");
names.add("A");

List<Person> filteredPersons = names.stream()
        .map(persons::get)
        .filter(Objects::nonNull)
        .collect(Collectors.toList());

如果personsnames的字母大小写可能不同,您可以在.toLowerCase()的密钥中执行Map

答案 2 :(得分:2)

您可以使用以下内容(未经测试):

.sorted((p1, p2) ->
        Integer.compare(names.indexOf(p1.getName()),
                        names.indexOf(p2.getName())))

并且,如上所述,收集到List而不是Set

正如Alexis在评论中提到的,你也可以更简洁地写出来:

.sorted(comparingInt(p -> names.indexOf(p.getName())))

comparingInt来自static import的{​​{1}}。