当前位置: 首页 > 面试题库 >

Java 8 Stream,头和尾

唐沈义
2023-03-14
问题内容

Java 8引入了一个类似于Scala的Stream的Stream类,它是一个强大的惰性构造,使用它可以非常简洁地执行以下操作:

def from(n: Int): Stream[Int] = n #:: from(n+1)

def sieve(s: Stream[Int]): Stream[Int] = {
  s.head #:: sieve(s.tail filter (_ % s.head != 0))
}

val primes = sieve(from(2))

primes takeWhile(_ < 1000) print  // prints all primes less than 1000

我想知道是否可以在Java 8中执行此操作,所以我写了如下代码:

IntStream from(int n) {
    return IntStream.iterate(n, m -> m + 1);
}

IntStream sieve(IntStream s) {
    int head = s.findFirst().getAsInt();
    return IntStream.concat(IntStream.of(head), sieve(s.skip(1).filter(n -> n % head != 0)));
}

IntStream primes = sieve(from(2));

相当简单,但它产生的java.lang.IllegalStateException: stream has already been operated upon or closed原因是findFirst()skip()都是终端操作,Stream只能在该终端上执行一次。

我真的不需要用完两次流,因为我需要的只是流中的第一个数字,其余作为另一个流,即Scala
Stream.head和的等效项Stream.tail。Java 8 Stream中是否可以使用一种方法来实现此目的?

谢谢。


问题答案:

即使您没有无法分割的问题IntStream,您的代码也不起作用,因为您是sieve递归地而不是惰性地调用方法。因此,在查询结果流中的第一个值之前,您必须进行无穷递归。

可以IntStream s将a分为头和尾IntStream(尚未消耗):

PrimitiveIterator.OfInt it = s.iterator();
int head = it.nextInt();
IntStream tail = IntStream.generate(it::next).filter(i -> i % head != 0);

在这个地方,您需要sieve懒惰地调用尾部的构造。Stream不提供;concat期望将现有流实例作为参数,并且您不能构造sieve带有lambda表达式的延迟调用的流,因为惰性创建仅在可变状态下才起作用,而lambda表达式不支持这种状态。如果您没有隐藏可变状态的库实现,则必须使用可变对象。但是,一旦您接受了可变状态的要求,该解决方案甚至比第一种方法更容易:

IntStream primes = from(2).filter(i -> p.test(i)).peek(i -> p = p.and(v -> v % i != 0));

IntPredicate p = x -> true;

IntStream from(int n)
{
  return IntStream.iterate(n, m -> m + 1);
}

这将递归地创建一个过滤器,但是最后无论创建一个IntPredicates的树还是一个s的树都没有关系IntStream(就像您的IntStream.concat方法可行一样)。如果您不喜欢过滤器的可变实例字段,则可以将其隐藏在内部类中(而不是在lambda表达式中)。



 类似资料:
  • 我是使用Java8流API的新手,但我希望使用它来解决以下问题。假设我有一个名为的POJO,它包含、和属性,这些属性可以表示以下内容的每一行记录: 看起来如下: 上述四条记录需要合并为两条按名称分组的记录,其中: 属性求和 属性求和 组合记录包括属性,该属性是和的累加和相乘的结果。 因此,上述结果将是: 一个名为的不同POJO将表示组合记录的每一行记录: 有哪些好的方法/解决方案可以将输入的列表转

  • 我有一节这样的课 如果我有一个此类对象的列表,如下所示

  • 问题内容: 假设您有一个txt文件,同时查看文件的前10行和后10行的命令是什么? 即,如果文件长200行,则可以一次性查看1-10行和190-200行。 问题答案: 您可以简单地: 如果由于某种原因需要使用管道,则如下所示: 注意:如果file.txt中的行数小于默认的head行+默认的tail行,则将打印重复的行。

  • 问题内容: 当我们需要调用Ajax请求时,我们要做的是: 我已经知道,使用,我们可以提出跨源请求 , 并且添加了ORIGIN标头。 题: 何时 添加此标头? 当浏览器(支持CORS)执行请求时是否添加?(跨域还是非跨域?) 还是在浏览器“看到”请求目标来源与当前来源不同时自动添加? 我的意思是:He **是什么意思? 跨域HTTP请求具有一个Origin头。该头为服务器提供请求的来源。此标头受浏览

  • 我正在尝试查找有关RabbitMQ使用的标准AMQP标头和AMQP协议保留的文档。我已经查看了AMQP文档和RabbitMQ的网站,但没有运气。是否有包含此信息的资源?谢谢。

  • 主要内容:1 HTTP Request Header请求头,2 HTTP Response Header 响应头本文列出了日常开发中常见的请求头和响应头,以供大家参考。 1 HTTP Request Header请求头 Header 说明 示例 Accept 指定客户端能够接收的内容类型 Accept: text/plain, text/html  Accept-Charset 浏览器可以接受的字符编码集。 Accept-Charset: iso-8859-5  Accept-Encoding