如何基于谓词创建Java实时子集合?
背景
我有一个接口:
public interface ThingRegistry { public Set<Thing> getAllThings(); public Set<Thing> getAllThingsWithProperty(String property); }
以及对应的实现类:
public class MemoryThingRegistry implements ThingRegistry { private final Set<Thing> things = new HashSet<>(); public Set<Thing> getAllThings() { return Collections.unmodifiableSet(this.things); } public Set<Thing> getAllThingsWithProperty(final String property) { return this.things.stream().filter((thing) -> thing.hasProperty(property)).collect(Collectors.toUnmodifiableSet()); } }
问题
getAllThings()返回的Set会同步注册表内的变更- 但
getAllThingsWithProperty()返回的Set无法同步这些变更
需求
能否使用Java标准库或常用第三方库,让getAllThingsWithProperty()返回一个“实时”子Set?即它基于原Set,每次访问时重新应用Predicate过滤?最好能适用于任意Collection(我另有一个用List的Registry接口)。我知道可以自定义Set实现,但希望避免这么做。
解决方案
1. 使用Guava库(推荐)
Guava提供的Sets.filter()(List场景对应Lists.filter())可以生成实时过滤的集合视图,该视图会在每次访问时自动对原集合应用过滤条件,原集合的增删改操作会实时反映到视图中。
修改后的实现代码:
import com.google.common.collect.Sets; import java.util.Collections; import java.util.function.Predicate; public class MemoryThingRegistry implements ThingRegistry { private final Set<Thing> things = new HashSet<>(); @Override public Set<Thing> getAllThings() { return Collections.unmodifiableSet(things); } @Override public Set<Thing> getAllThingsWithProperty(final String property) { Predicate<Thing> propertyPredicate = thing -> thing.hasProperty(property); // 生成实时过滤视图后,包装为不可变集合防止外部修改 return Collections.unmodifiableSet(Sets.filter(things, propertyPredicate)); } }
对于List类型的Registry,只需将Sets.filter()替换为Lists.filter()即可,逻辑完全一致。
2. 使用Apache Commons Collections
Apache Commons Collections的FilteredSet(List场景对应FilteredList)也支持创建实时过滤的集合视图,用法如下:
import org.apache.commons.collections4.set.FilteredSet; import java.util.Collections; import java.util.function.Predicate; public class MemoryThingRegistry implements ThingRegistry { private final Set<Thing> things = new HashSet<>(); @Override public Set<Thing> getAllThings() { return Collections.unmodifiableSet(things); } @Override public Set<Thing> getAllThingsWithProperty(final String property) { Predicate<Thing> propertyPredicate = thing -> thing.hasProperty(property); // 创建实时过滤的Set视图,再包装为不可变 return Collections.unmodifiableSet(FilteredSet.filteredSet(things, propertyPredicate::test)); } }
3. Java标准库的折中方案
Java标准库本身没有提供直接的实时过滤集合视图,如果不想引入第三方库,只能采用两种折中方式:
- 保持原实现,每次调用
getAllThingsWithProperty()时重新过滤生成新集合,但无法实现“实时同步” - 提供返回
Stream<Thing>的方法,让调用方自行处理过滤逻辑,但不符合返回Set的需求
内容的提问来源于stack exchange,提问作者Scroobius
相关产品推荐
相关产品推荐

