Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/17.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 在异步生成的元素流上使用hasNext()和next()进行迭代_Java_Scala_Asynchronous_Iterator_Java Stream - Fatal编程技术网

Java 在异步生成的元素流上使用hasNext()和next()进行迭代

Java 在异步生成的元素流上使用hasNext()和next()进行迭代,java,scala,asynchronous,iterator,java-stream,Java,Scala,Asynchronous,Iterator,Java Stream,我必须使用hasNext()和next()方法实现一个迭代器接口(由Java API定义),该接口应该返回源自异步处理的HTTP响应(使用Akka actors处理)的结果元素 必须满足以下要求: 不要阻塞并等待异步操作完成,因为生成大型结果集可能需要一段时间(迭代器应在结果元素可用时立即返回结果元素) Iterator.next()应该阻塞,直到下一个元素可用为止(如果没有更多的元素,则抛出异常) Iterator.hasNext()应该返回true,只要还有更多元素(即使下一个元素还不可用

我必须使用hasNext()和next()方法实现一个迭代器接口(由Java API定义),该接口应该返回源自异步处理的HTTP响应(使用Akka actors处理)的结果元素

必须满足以下要求:

  • 不要阻塞并等待异步操作完成,因为生成大型结果集可能需要一段时间(迭代器应在结果元素可用时立即返回结果元素)
  • Iterator.next()应该阻塞,直到下一个元素可用为止(如果没有更多的元素,则抛出异常)
  • Iterator.hasNext()应该返回true,只要还有更多元素(即使下一个元素还不可用)
  • 结果的总数事先未知。结果生成参与者将在完成时发送特定的“结束消息”
  • 尽量避免使用InterruptedException,例如,当迭代器正在等待一个空队列,但不会生成更多的元素时
我还没有研究过Java8流或Akka流。但由于我基本上必须在队列(有限流)上迭代,我怀疑是否有合适的解决方案

目前,我的Scala实现存根使用java.util.concurrent.BlockingQueue,如下所示:

类ResultStreamIterator扩展迭代器[Result]{
val resultQueue=new ArrayBlockingQueue[选项[结果]](100)
def hasNext():Boolean=???//如果尚未完成,则返回true
def next():结果=???//如果尚未完成,则获取()下一个元素
案例类结果(值:Any)//由结果生成参与者发送
案例对象已完成//完成时由结果生成参与者发送
类ResultCollector扩展了Actor{
def接收={
案例结果(值)=>resultQueue.put(部分(值))
案例完成=>resultQueue.put(无)
}
}
}
我使用一个选项[Result]来表示结果流的结尾,但没有。我已经尝试过偷看下一个元素并使用“完成”标志,但我希望有一个更简单的解决方案

奖金问题:

  • 单元测试如何覆盖同步/异步实现,尤其是测试延迟的结果生成
  • 如何使迭代器成为线程安全的

    • 以下代码将确保要求的安全性。 演员的字段可以在演员的接收器中安全地修改。 所以resultQueue不应该在迭代器的字段中,而应该在Actor的字段中

      // ResultCollector should be initialized.
      // Initilize code is like...
      // resultCollector ! Initialize(100)
      class ResultStreamIterator(resultCollector: ActorRef) extends Iterator[Result] {
      
        implicit val timeout: Timeout = ???
      
        override def hasNext(): Boolean = Await.result(resultCollector ? HasNext, Duration.Inf) match {
          case ResponseHasNext(hasNext) => hasNext
        }
      
        @scala.annotation.tailrec
        final override def next(): Result = Await.result(resultCollector ? RequestResult, Duration.Inf) match {
          case ResponseResult(result) => result
          case Finished => throw new NoSuchElementException("There is not result.")
          case WaitingResult => next()// should be wait for a moment.
        }
      
      }
      
      case object RequestResult
      case object HasNext
      
      case class ResponseResult(result: Result)
      case class ResponseHasNext(hasNext: Boolean)
      case object Finished
      case object WaitingResult
      
      case class Initialize(expects: Int)
      
      // This code may be more ellegant if using Actor FSM
      // Acotr's State is (beforeInitialized)->(collecting)->(allCollected)
      class ResultCollector extends Actor with Stash {
      
        val results = scala.collection.mutable.Queue.empty[Result]
      
        var expects = 0
      
        var counts = 0
      
        var isAllCollected = false
      
        def beforeInitialized: Actor.Receive = {
          case Initialize(n) =>
            expects = n
            if (expects != 0) context become collecting
            else context become allCollected
            unstashAll
          case _ => stash()
        }
      
        def collecting: Actor.Receive = {
          case RequestResult =>
            if (results.isEmpty) sender ! WaitingResult
            else sender ! ResponseResult(results.dequeue())
          case HasNext => ResponseHasNext(true)
          case result: Result =>
            results += result
            counts += 1
            isAllCollected = counts >= expects
            if (isAllCollected) context become allCollected
        }
      
        def allCollected: Actor.Receive = {
          case RequestResult =>
            if (results.isEmpty) sender ! Finished
            else sender ! ResponseResult(results.dequeue())
          case HasNext => ResponseHasNext(!results.isEmpty)
        }
      
        def receive = beforeInitialized
      }
      

      您可以使用变量存储下一个元素,并在两种方法的开头等待它:

      private var nextNext: Option[Result] = null
      
      def hasNext(): Boolean = {
        if (nextNext == null) nextNext = resultQueue.take()
        return !nextNext.isEmpty
      }
      
      def next(): Result = {
        if (nextNext == null) nextNext = resultQueue.take()
        if (nextNext.isEmpty) throw new NoSuchElementException()
        val result = nextNext.get
        nextNext = null
        return result
      }
      

      我按照大郎的建议做了一些必要的修改。一般来说,我喜欢将
      getNext()
      next()
      实现为
      ask
      消息发送给参与者的方法。这可以确保在任何时候只有一个线程修改队列

      但是,我不确定此实现的性能,因为
      ask
      wait。result
      将为
      hasNext()
      next()的每次调用创建两个线程

      导入scala.concurrent.{wait,Future}
      导入scala.concurrent.duration_
      导入scala.language.postfix操作
      导入akka.actor.{ActorRef,ActorSystem,Props,Stash}
      导入akka.pattern.ask
      导入akka.util.Timeout
      case对象HasNext
      案例对象GetNext
      案例类结果(值:任意)
      案例对象完成
      类ResultCollector使用Stash扩展了Actor{
      val queue=scala.collection.mutable.queue.empty[结果]
      def收集:参与者。接收={
      case HasNext=>if(queue.isEmpty)stash else sender!true
      case GetNext=>if(queue.isEmpty)stash else sender!queue.dequeue
      案例值:结果=>unstashAll;队列+=value
      case Done=>unstashAll;上下文变为服务
      }
      def服务:参与者。接收={
      case HasNext=>sender!queue.nonEmpty
      case GetNext=>sender!{if(queue.nonEmpty)queue.dequeue else new NoSuchElementException}
      }
      def接收=收集
      }
      类ResultStreamIteration(resultCollector:ActorRef)扩展了迭代器{
      隐式val超时:超时=超时(30秒)
      override def hasNext():Boolean=Await.result(resultCollector?hasNext,Duration.Inf)匹配{
      案例b:布尔=>b
      }
      override def next():Any=Await.result(resultCollector?GetNext,Duration.Inf)匹配{
      案例结果(值:任意)=>值
      案例e:Throwable=>throw e
      }
      }
      对象测试扩展应用程序{
      隐式val exec=scala.concurrent.ExecutionContext.global
      val system=ActorSystem.create(“测试”)
      val-actorRef=system.actorOf(Props[ResultCollector])
      未来{
      
      例如(如果你使用Java 8,实现一个
      Spliterator
      StreamSupport.stream(你的Spliterator,false/true)
      你打算使用Java还是Scala?@Bubletan我更喜欢Scala。但是算法在Java和Scala中应该没什么关系(除了语法)。@fge我认为BlockingQueue.Spliterator()只需为队列中当前的元素创建一个迭代器。或者它是否也会像底层BlockingQueue一样阻塞?@goerlitz当队列为空时,您如何知道是否还会有实际结果?如果
      hasNext()
      当队列为空时返回true,但在向其添加
      None
      之后,接下来应该做什么
      return?感谢您提示将队列放入Actor中。因此,正如您所说,它更安全、更干净,因为对队列的所有访问都是通过Actor的事件处理程序同步的。然后使用
      ask
      请求、不同的状态也是有意义的(我必须查看FSM),和
      隐藏
      。如果队列是参与者的字段,则不必使用BlockingQueue。如果队列是迭代器的字段,则实现迭代器的方法
      import scala.concurrent.{Await, Future}
      import scala.concurrent.duration._
      import scala.language.postfixOps
      
      import akka.actor.{ActorRef, ActorSystem, Props, Stash}
      import akka.pattern.ask
      import akka.util.Timeout
      
      case object HasNext
      case object GetNext
      
      case class Result(value: Any)
      case object Done
      
      class ResultCollector extends Actor with Stash {
      
        val queue = scala.collection.mutable.Queue.empty[Result]
      
        def collecting: Actor.Receive = {
          case HasNext       => if (queue.isEmpty) stash else sender ! true
          case GetNext       => if (queue.isEmpty) stash else sender ! queue.dequeue
          case value: Result => unstashAll; queue += value
          case Done          => unstashAll; context become serving
        }
      
        def serving: Actor.Receive = {
          case HasNext => sender ! queue.nonEmpty
          case GetNext => sender ! { if (queue.nonEmpty) queue.dequeue else new NoSuchElementException }
        }
      
        def receive = collecting
      }
      
      class ResultStreamIteration(resultCollector: ActorRef) extends Iterator {
      
        implicit val timeout: Timeout = Timeout(30 seconds)
      
        override def hasNext(): Boolean = Await.result(resultCollector ? HasNext, Duration.Inf) match {
          case b: Boolean => b
        }
      
        override def next(): Any = Await.result(resultCollector ? GetNext, Duration.Inf) match {
          case Result(value: Any) => value
          case e: Throwable       => throw e
        }
      }
      
      object Test extends App {
        implicit val exec = scala.concurrent.ExecutionContext.global
        val system = ActorSystem.create("Test")
        val actorRef = system.actorOf(Props[ResultCollector])
        Future {
          for (i <- 1 to 10000) actorRef ! Result(s"Result $i"); actorRef ! Done
        }
        val iterator = new ResultStreamIteration(actorRef)
        while (iterator.hasNext()) println(iterator.next)
        system.shutdown()
      }