Java实现惰性集合:不改返回Stream优化集合按需遍历
可以实现,无需修改返回类型为Stream
Java里确实能在保持返回Collection类型的前提下实现惰性加载,核心思路是自定义一个实现Collection接口的惰性集合类,让它的迭代器仅在需要元素时才执行耗时操作(比如数据库查询),而不是提前生成所有元素。
实现原理
Collection的默认实现(比如ArrayList、HashSet)都是饥饿加载的——创建时就会生成并存储所有元素。但我们可以自己实现Collection,让它的iterator()方法返回一个惰性迭代器:只有当调用next()时才去生成下一个元素,遍历到满足条件的元素后就停止后续生成。
具体代码实现
1. 自定义惰性集合类
这个类只实现Collection中必要的方法,重点是确保迭代器和stream的惰性:
import java.util.*; import java.util.function.Supplier; import java.util.stream.Stream; import java.util.stream.StreamSupport; public class LazyCollection<T> implements Collection<T> { private final Supplier<Iterator<T>> iteratorSupplier; public LazyCollection(Supplier<Iterator<T>> iteratorSupplier) { this.iteratorSupplier = iteratorSupplier; } @Override public Iterator<T> iterator() { return iteratorSupplier.get(); } @Override public Stream<T> stream() { // 基于惰性迭代器创建流,保持惰性 return StreamSupport.stream( Spliterators.spliteratorUnknownSize(iterator(), Spliterator.ORDERED), false ); } @Override public boolean contains(Object o) { // 按需遍历,找到元素就停止 Iterator<T> iter = iterator(); while (iter.hasNext()) { T elem = iter.next(); if (Objects.equals(elem, o)) { return true; } } return false; } // 其他不需要的Collection方法直接抛出UnsupportedOperationException @Override public int size() { throw new UnsupportedOperationException("惰性集合不支持size()方法"); } @Override public boolean isEmpty() { return !iterator().hasNext(); } @Override public boolean add(T t) { throw new UnsupportedOperationException(); } @Override public boolean remove(Object o) { throw new UnsupportedOperationException(); } @Override public boolean containsAll(Collection<?> c) { for (Object o : c) { if (!contains(o)) return false; } return true; } @Override public boolean addAll(Collection<? extends T> c) { throw new UnsupportedOperationException(); } @Override public boolean removeAll(Collection<?> c) { throw new UnsupportedOperationException(); } @Override public boolean retainAll(Collection<?> c) { throw new UnsupportedOperationException(); } @Override public void clear() { throw new UnsupportedOperationException(); } @Override public Object[] toArray() { throw new UnsupportedOperationException(); } @Override public <E> E[] toArray(E[] a) { throw new UnsupportedOperationException(); } }
2. 修改getInts方法返回惰性集合
原来的getInts可能提前生成所有元素,现在改成返回LazyCollection,只在迭代时执行耗时操作:
public Collection<Integer> getInts() { return new LazyCollection<>(() -> new Iterator<Integer>() { private int currentId = 0; // 假设原本要生成100个元素,这里定义总数 private static final int MAX_ID = 100; @Override public boolean hasNext() { return currentId < MAX_ID; } @Override public Integer next() { if (!hasNext()) throw new NoSuchElementException(); // 仅在调用next()时执行耗时的数据库查询 Integer result = fetchObjectFromDb(currentId); currentId++; return result; } }); } // 模拟耗时的数据库解析操作 private Integer fetchObjectFromDb(int id) { try { // 模拟耗时 Thread.sleep(100); System.out.println("生成元素:" + id); return id; } catch (InterruptedException e) { throw new RuntimeException(e); } }
3. 调用primeExists方法验证
假设primeExists是检查集合中是否存在质数,此时只会生成到第一个质数就停止:
public boolean primeExists() { // 用stream的anyMatch,会惰性遍历 return getInts().stream().anyMatch(this::isPrime); } private boolean isPrime(int num) { if (num <= 1) return false; for (int i = 2; i <= Math.sqrt(num); i++) { if (num % i == 0) return false; } return true; }
注意事项
- 这种方式依赖调用方使用惰性遍历的方法(比如迭代器、
stream().anyMatch()、contains()),如果调用方使用toArray()、size()这类需要全量元素的方法,会触发所有耗时操作,甚至抛出异常。 - 尽量只实现
Collection中必要的方法,不需要的方法直接抛出UnsupportedOperationException,避免破坏惰性语义。
内容的提问来源于stack exchange,提问作者Ricky Sixx
相关产品推荐
相关产品推荐

