我有一个方法,它通过执行一些代价高昂的操作(例如:使用数据库来通过ID解析对象)。
让我们使用这个方法作为一个例子:
public static Collection<Integer> getInts()
{
return IntStream.range(1, 100)
.peek((value) -> System.out.printf("getInts(): iterating over [%d]\n", value))
.boxed()
.collect(Collectors.toList());
}
然后我有另一个方法,它检查集合中是否存在与 predicate 匹配的对象:
public static boolean primeExists()
{
return getInts().stream()
.peek((value) -> System.out.printf("primeExists(): iterating over [%d]\n", value))
.anyMatch(Main::isPrime); // checks if there is a prime number
}
通过运行这段代码,我注意到getInts()
方法中的peek
打印了10次,每个数字打印一次,尽管primeExists()
实际上只需要前两个元素。
下面是一个完整的代码示例:
public class Main
{
private static boolean isPrime(int n)
{
if (n < 2)
{
return false;
}
for (int i = 2; i * i <= n; i++)
{
if (n % i == 0)
{
return false;
}
}
return true;
}
public static Collection<Integer> getInts()
{
return IntStream.range(1, 10)
.peek((value) -> System.out.printf("getInts(): iterating over [%d]\n", value))
.boxed()
.collect(Collectors.toList());
}
public static boolean primeExists()
{
return getInts().stream()
.peek((value) -> System.out.printf("primeExists(): iterating over [%d]\n", value))
.anyMatch(Main::isPrime);
}
public static void main(String[] args) throws IOException
{
primeExists();
}
}
这是输出:
getInts(): iterating over [1]
getInts(): iterating over [2]
getInts(): iterating over [3]
getInts(): iterating over [4]
getInts(): iterating over [5]
getInts(): iterating over [6]
getInts(): iterating over [7]
getInts(): iterating over [8]
getInts(): iterating over [9]
primeExists(): iterating over [1]
primeExists(): iterating over [2]
如何更改此代码以获得此输出:
getInts(): iterating over [1]
getInts(): iterating over [2]
primeExists(): iterating over [1]
primeExists(): iterating over [2]
有没有可能在不更改getInts()
方法以返回中间Stream
的情况下获得这种行为?
我认为理想的解决方案是从getInts()
返回一个惰性集合(例如:其元素仅在实际迭代时创建的集合)。有没有一种方法可以在Java中创建这样的集合?
2条答案
按热度按时间ibrsph3r1#
否
好吧,我已经回答了问题。但让我们提供一大堆上下文:
我认为理想的解决方案是从getInts()返回一个惰性集合(例如:其元素仅在实际迭代时创建的集合)。有没有一种方法可以在Java中创建这样的集合?
你对术语感到困惑。通常在官方定义的系统中(编程语言的定义肯定比通常的人与人之间的对话要具体得多),术语获得了更具体,更详细的定义含义。
在你的头脑中,“收藏”可能是懒惰的,为什么不呢,对吗?收藏是一个词,在字典里,字典里没有什么规定它必须是渴望的。
但是在java中,*
j.u.Collection
意味着eager*。要求一个懒惰的收集,“收集”在那里指的是英语单词,这是有道理的。要求'一个懒惰的集合',其中集合具体引用java.util.Collection
,没有意义。这就像要求:“一个圆,但有一个角。这是矛盾修饰法考虑到你是在java的土地上,使用单词'collection'并打算将其作为字典单词而不是j.u.Collection
是一个错误:语言是用来交流思想的,如果语言不能很好地表达思想,那你就选择了不好的语言。很自然地,试图消除“collection - in the sense of英语dictionary”和“collection - in the sense of j.u. Collection”的歧义几乎是不可能的,所以找另一个词来表达这个概念。那么,java的官方术语是什么呢?为
Stream
。换句话说,你有一个方法,它的返回类型是“EagerCollection”(除了在java中我们只称之为Collection),并要求:有没有办法让你不那么急切?嗯,不。规格是字面上告诉你,它必须是渴望。
考虑一个假设的“懒惰集合”的API,并通过
j.u.Collection
的API来获得灵感,这是没有意义的。许多API需要:size()
被指定为返回-1表示无限集合,返回-2表示'I have no idea,I might even be infinite'。这就是为什么
Stream
存在:需要不同的API。这就是 * 为什么 *java.util.Collection
(特别是java.util.List
)本质上意味着“渴望”。即使有一些东西(实际上,只有iterator()
,几乎所有其他方法都是无意义的,或者不能以快速和非破坏性的方式实现)也可以应用于“懒惰集合”。作为一个简单的例子,
.get(1000)
通常涉及以下两种销毁:(我可以给予你第1000个元素,但这样做,元素0-999被消耗,永远不会再返回,所以如果你之后调用.get(500)
,将不得不发生异常),或缓存(我将分配一些内存并提取1000个元素,缓存0-999,这样.get(500)
以后就可以工作了,尽管它是从缓存中抓取的)。相反,在流中,你会写
.skip(1000).findFirst()
,它同样简单,而且它实际上做的事情要准确得多-而对于.get(1000)
,我不知道这是否会创建缓存或崩溃,或者它是如何工作的,我必须阅读文档,对于.skip(1000).findFirst()
,从那一点点文本中就可以明显看出:这将破坏元素0-999(从源中取出它们并丢弃它们,我永远无法返回到这些)。l2osamch2#
您需要返回
Stream
而不是Collection
。因为在生成集合时,它将从流中读取所有值。所以pick
被调用了10次。更新程序。
输出:
因为它将获取一个元素并处理它,所以它将为每个元素打印
getInts()
和primeExists()
。