-1

The following code throws an UnsupportedOperationException with Scala 2.12.7 and Java 11.0.1.

listBuffer
.iterator
.dropWhile(_ != u)
.asJava
.remove()

Why? ListBuffer is mutable, and using an Iterator to remove elements while traversing should be valid. The ListBuffer contains Int.

java.lang.UnsupportedOperationException was thrown.
java.lang.UnsupportedOperationException
    at scala.collection.convert.Wrappers$IteratorWrapper.remove(Wrappers.scala:31)
    at scala.collection.convert.Wrappers$IteratorWrapper.remove(Wrappers.scala:26)
    at week4.UndirectedGraph.$anonfun$removeVertex$2(UndirectedGraph.scala:52)
    at scala.runtime.java8.JFunction1$mcVI$sp.apply(JFunction1$mcVI$sp.java:12)
    at scala.collection.immutable.List.foreach(List.scala:388)
    at scala.collection.generic.TraversableForwarder.foreach(TraversableForwarder.scala:34)
    at scala.collection.generic.TraversableForwarder.foreach$(TraversableForwarder.scala:34)
    at scala.collection.mutable.ListBuffer.foreach(ListBuffer.scala:43)
    at week4.UndirectedGraph.$anonfun$removeVertex$1(UndirectedGraph.scala:50)

Edit: Not a duplicate of this question, since the problem with that is trying to structurally modify the list returned by Arrays.asList. I shouldn't have to state the obvious, if not for the people who don't know the answer, but are usually the first to try to close a question as duplicate.

Abhijit Sarkar
  • 21,927
  • 20
  • 110
  • 219

2 Answers2

1

Here is the source code throwing the error:

 case class IteratorWrapper[A](underlying: Iterator[A]) extends ju.Iterator[A] with ju.Enumeration[A] {
    def hasNext = underlying.hasNext
    def next() = underlying.next()
    def hasMoreElements = underlying.hasNext
    def nextElement() = underlying.next()
    override def remove() = throw new UnsupportedOperationException
  }

Note that it doesn't care whether the underlying collection is mutable or not.

I think this implementation makes sense. Scala iterators do not support remove() by design; their conversion to Java should also yield iterators that behave the same way.

jrook
  • 3,459
  • 1
  • 16
  • 33
  • “Iterators do not support `remove` by design” - do you’ve a reference for this? I’m not seeing any such statement in the [docs](https://docs.scala-lang.org/overviews/collections/iterators.html) – Abhijit Sarkar Nov 20 '18 at 12:24
  • @AbhijitSarkar, 1) There is no mention of `remove` in the [Scaladocs](https://www.scala-lang.org/api/2.12.1/scala/collection/Iterator.html). 2) The [Iterator trait](https://github.com/scala/scala/blob/2787b47396013a44072fa7321482103b66fbccd3/src/library/scala/collection/Iterator.scala#L348) does not define `remove`. – jrook Nov 20 '18 at 15:41
-1

Not a direct answer to your question, but a solution, in case you only need to remove elements based on object equality, just like in your example code:

Use -= method of the ListBuffer. This removes all occurences of the element from ListBuffer, without creating a copy of the structure.

  def main(args: Array[String]): Unit = {
    val u = 999
    val listBuffer = new ListBuffer[Int]
    listBuffer ++= Seq(1, 7, 3, 8, 0, 3, 6, 7, 999, 5, 7, 8, 999, 1, 5, 999)
    println(listBuffer)
    listBuffer -= u
    println(listBuffer)
  }

If you used filter method, than you can use a predicate, but a copy of the ListBuffer would be created:

  def main(args: Array[String]): Unit = {
    val u = 999
    var listBuffer = new ListBuffer[Int]
    listBuffer ++= Seq(1, 7, 3, 8, 0, 3, 6, 7, 999, 5, 7, 8, 999, 1, 5, 999)
    println(listBuffer)
    listBuffer = listBuffer.filter(_ != u)
    println(listBuffer)
  }
ygor
  • 1,726
  • 1
  • 11
  • 23