使用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
实施确保维持订单。
答案 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());
如果persons
和names
的字母大小写可能不同,您可以在.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}}。