如何处理kotlin中迭代器实现中的可空值?

如何处理kotlin中迭代器实现中的可空值?,kotlin,data-structures,linked-list,Kotlin,Data Structures,Linked List,因此,我遵循Sedgewick的算法书,尝试将实现从Java转换为Kotlin,当我尝试为Bag数据结构实现一个迭代器(本质上是一个单向链表)时,我遇到了Kotlin中的可空性问题和线程安全问题 本书中的java实现是这样完成的: public class Bag<Item> { private Node first; private class Node { Item item; Node next; } /* some me

因此,我遵循Sedgewick的算法书,尝试将实现从Java转换为Kotlin,当我尝试为
Bag
数据结构实现一个迭代器(本质上是一个单向链表)时,我遇到了Kotlin中的可空性问题和线程安全问题

本书中的java实现是这样完成的:

public class Bag<Item> {
   private Node first;

   private class Node {
       Item item;
       Node next;
   }

   /* some methods */

   private class Iterator<Item> {
      private Node current = first;

      public boolean hasNext() { current != null; }

      public Item next() {
          if (!hasNext()) throw new NoSuchElementException();
          Item item = current.item;
          current = current.next;
          return item;
      }
   }
}
公共类包{
私有节点优先;
私有类节点{
项目;
节点下一步;
}
/*一些方法*/
私有类迭代器{
私有节点电流=第一;
公共布尔值hasNext(){current!=null;}
公共项目下一步(){
如果(!hasNext())抛出新的NoSuchElementException();
项目=当前项目;
当前=当前。下一步;
退货项目;
}
}
}
我试着在Kotlin中实现如下:

class Bag<Item> : Iterable<Item> {
   private inner class Node(val item: Item, val next: Node?)

   private var first : Node? = null

   /* Some methods */

   override fun iterator() = object : Iterator<Item> {
      private var current : Bag<Item>.Node? = first

      override fun hasNext() : Boolean = current != null

      override fun next() : Item {
         if (current == null) throw NoSuchElementException()

         val item = current.item
         current = current.next
         return item
      }
   }
}
类包:Iterable{
私有内部类节点(val项:项,val下一个:节点?)
private var first:节点?=null
/*一些方法*/
重写有趣的迭代器()=对象:迭代器{
专用无功电流:包节点?=第一个
override fun hasNext():Boolean=current!=null
覆盖下一步():项{
如果(当前==null)抛出NoTouchElementException()
val项目=当前项目
当前=当前。下一步
退货项目
}
}
}
但我得到了以下错误:

无法对“Bag.Node”进行智能转换,因为“current”是一个可变属性,此时可能已更改

我理解这是由于检查变量是否为null和实际访问变量属性之间的竞争条件造成的,因为其他线程可能会将变量设置为null。一段时间后,我进入了以下实施阶段:

override fun iterator() = object : Iterator<Item> {
   private var current : Bag<Item>.Node? = first

   override fun hasNext() : Boolean = current != null

   override fun next() : Item {
      current?.let {
         val item = it.item
         current = it.next
         return item
      } ?: throw NoSuchElementException()
   }
}
override fun iterator()=对象:iterator{
专用无功电流:包节点?=第一个
override fun hasNext():Boolean=current!=null
覆盖下一步():项{
现在?,让我来{
val item=it.item
当前=it.next
退货项目
}?:抛出NoTouchElementException()
}
}
编译器认为这很好。但我仍然有一些疑问。这就引出了我的问题:

1)是赋值
current=it。下一步
是线程安全的,还是应该将其赋值给隐式
it

2)是否有一种惯用的Kotlin方法来实现以空值结尾的不可空类型的迭代器?(意味着除结束条件外,其中的所有值均为非空)

分配是否为当前=it.next线程安全

它不是线程安全的

想象一个整数列表和两个线程
a
B
谁想要使用迭代器实例
I

1 -> 2 -> 3 -> 4 -> 5            A: item=1, next=(2)
^                                A: item=1, next=(2)
I
两个线程都开始迭代。
当前?内的两条路径。让
。读取当前项(
val item=it.item
)并获取
item=1,next=(2)
。然后,第一个线程
A
被冻结,第二个线程
B
将迭代器向前推进三项:

1 -> 2 -> 3 -> 4 -> 5            A: item=1, next=(2)
               ^                 B: item=4, next=(5)
               I
现在
B
进入
let
并读取下一个
item
item=4,next=(5)
。记住,
A
仍然在他的循环中,它的
item
item=1,next=(2)
。如果
B
现在被冻结,并且
A
前进一行代码(
current=it.next
),那么事情就被打破了:
current
是一个共享状态(存储在迭代器中),因此,
B
也会看到更改。在
B
中的下一次迭代中,它将“返回”到项目2。不会发生什么坏事,程序也不会失败,但很可能这不是您需要的行为

更重要的是:由于上述原因,迭代器并不意味着线程安全,每个线程都应该有自己的独立迭代器。对于更改集合(插入/删除)的迭代器来说,事情变得更加有趣,但这是关于集合的另一个故事,而不是迭代器

我是否应该将其指定给隐式it

不能为其赋值,因为它是一个函数参数,并且是通过值传递的,因此不能更改。编译器将禁止带有“Val无法重新分配”消息的分配

是否有一种惯用的Kotlin方法来实现以null值结尾的不可为null类型的迭代器

我会说:是的。您可以使用来指定不同类型的节点,例如:

sealed class Node<out T>;

object Empty : Node<Nothing>();

data class Full<T>(val item: T, val next: Node<T>) : Node<T>();

class Bag<T>(private val first: Node<T>) : Iterable<T> {
    override fun iterator(): Iterator<T> = object : Iterator<T> {
        private var current = first

        override fun hasNext() = current !is Empty

        override fun next() = when (val c = current) {
            Empty -> throw IllegalStateException()
            is Full -> {
                current = c.next
                c.item
            }
        }
    }
}

fun main() {
    val bag = Bag(Full(1, Full(2, Full(3, Empty))))

    bag.forEach(::println)
}
密封类节点;
对象为空:Node();
数据类已满(val项:T,val下一步:节点):Node();
类包(private val first:节点):Iterable{
重写有趣的迭代器():迭代器=对象:迭代器{
专用无功电流=第一
override fun hasNext()=current!为空
override fun next()=何时(val c=当前){
空->抛出非法状态异常()
已满->{
电流=c
c、 项目
}
}
}
}
主要内容(){
val行李=行李(满(1,满(2,满(3,空)))
行李托运人(::println)
}

我建议您在那里使用自己的迭代器,(您可以处理这些空值)预定义的迭代器函数可能没有线程安全机制