¿Cómo declarar una lista vacía y luego agregar una cadena en Scala?

81

Tengo un código como este:

val dm  = List[String]()
val dk = List[Map[String,Object]]()

.....

dm.add("text")
dk.add(Map("1" -> "ok"))

pero arroja java.lang.UnsupportedOperationException en tiempo de ejecución.

Necesito declarar una lista vacía o mapas vacíos y en algún lugar más adelante en el código necesito completarlos.

rjc
fuente
¿Qué te hace pensar que hay una addoperación List?
Debilski
Si desea utilizar la operación de adición, deberá declarar una ArrayList. Los valores en scala son esencialmente inmutables, por lo que no se pueden agregar.
Phantom73
1
iirc val es más como final, puede agregarles si usa las colecciones mutables. por ejemplo, scala-lang.org/api/current/scala/collection/mutable/…
DaVinci
1
@rjc ¿Qué versión de scala estás usando? El mío (2.9.0) me da un error de compilación.
paradigmático
4
¿Importaste scala.collection.JavaConversions? Si lo hizo, está viendo la razón por la que recomiendo en su JavaConverterslugar: dmy dkse está convirtiendo en una colección de Java, y luego el addmétodo llamado en esa colección. Peor aún, dmy dkno se están modificando, incluso si no recibió un error. Y, por cierto, el error es que no 1 -> "ok"es Map[Int,String]así Map[String, Object].
Daniel C. Sobral

Respuestas:

117

Las listas de Scala son inmutables por defecto. No puede "agregar" un elemento, pero puede formar una nueva lista agregando el nuevo elemento al frente. Dado que es una lista nueva , debe reasignar la referencia (por lo que no puede usar un val).

var dm  = List[String]()
var dk = List[Map[String,AnyRef]]()

.....

dm = "text" :: dm
dk = Map(1 -> "ok") :: dk

El operador ::crea la nueva lista. También puede utilizar la sintaxis más corta:

dm ::= "text" 
dk ::= Map(1 -> "ok")

NB: En scala no use el tipo Objectpero Any, AnyRefo AnyVal.

paradigmático
fuente
Muy buena respuesta, pero ¿puede decir si declaro una lista como en su respuesta, son de tipo scala.collections.mutable o inmutable? REPL no dejó esto claro.
rjc
2
Por defecto. Si no importa nada. Listes inmutable. Ese es el recomendado para la mayoría de los usos.
paradigmático
11
@rjc Scala no tiene un mutable.List- Listes un tipo concreto, del cual la única implementación es inmutable. Hay clases inmutables como LinkedListy DoubleLinkedList, que en su mayoría son clases auxiliares. El equivalente de Scala de Java ArrayListes ArrayBuffer, y el equivalente de Java LinkedListes ListBuffer. El rasgo que corresponde a Java Listes Seq- del cual hay collection.Seqy, extendiéndolo, collection.immutable.Seqy collection.mutable.Seq.
Daniel C. Sobral
@paradigmatic ¿hay alguna diferencia entre ::=y +=?
Mahdi
@Mahdi Puede haber una diferencia. Solo en listas ::está definido, por +=lo que no funcionará. En otra colección (no en lib estándar): si se implementan ::=o +=, se usará la implementación. De lo contrario, el compilador se convertirá x::=yen x = y::xe x+=yinro x=x+y. En el segundo caso, son iguales si la implementación de ::es la misma que la implementación de +...
paradigmático
17

Si necesita mutar cosas, use ArrayBuffero en su LinkedBufferlugar. Sin embargo, sería mejor abordar esta declaración:

Necesito declarar una lista vacía o mapas vacíos y en algún lugar más adelante en el código necesito completarlos.

En lugar de hacer eso, llene la lista con código que devuelva los elementos. Hay muchas formas de hacerlo, y daré algunos ejemplos:

// Fill a list with the results of calls to a method
val l = List.fill(50)(scala.util.Random.nextInt)

// Fill a list with the results of calls to a method until you get something different
val l = Stream.continually(scala.util.Random.nextInt).takeWhile(x => x > 0).toList

// Fill a list based on its index
val l = List.tabulate(5)(x => x * 2)

// Fill a list of 10 elements based on computations made on the previous element
val l = List.iterate(1, 10)(x => x * 2)

// Fill a list based on computations made on previous element, until you get something
val l = Stream.iterate(0)(x => x * 2 + 1).takeWhile(x => x < 1000).toList

// Fill list based on input from a file
val l = (for (line <- scala.io.Source.fromFile("filename.txt").getLines) yield line.length).toList
Daniel C. Sobral
fuente
14

Como ya han mencionado todos, esta no es la mejor forma de usar listas en Scala ...

scala> val list = scala.collection.mutable.MutableList[String]()
list: scala.collection.mutable.MutableList[String] = MutableList()

scala> list += "hello"
res0: list.type = MutableList(hello)

scala> list += "world"
res1: list.type = MutableList(hello, world)

scala> list mkString " "
res2: String = hello world
agilesteel
fuente
¿Puede decir si su lista se declara como en su respuesta, dará un mejor rendimiento en tiempo de ejecución en lugar de responder por paradigmético? Suponga que se agregarían millones de elementos a la lista.
rjc
Depende de lo que intente lograr. Recomendaría comenzar con uno inmutable como sugirió @paradigmatic. La complejidad de agregar un elemento a una lista inmutable como esta: list ::= "text"es O (1) que es constante y lo mejor que puede hacer.
agilesteel
rjc: contras de listas inmutables es O (1); sin embargo, lo que realmente importa es su patrón de acceso en lo que respecta a la eficiencia. Por ejemplo, si el orden es importante y debe crear la lista agregando, Vector es una mejor opción (inmutable).
Kris Nuttycombe
6

Como se mencionó en una respuesta anterior , Scala List es una colección inmutable. Puede crear una lista vacía con .empty[A]. A continuación, puede utilizar un método :+, +:o ::con el fin de añadir elementos a la lista.

scala> val strList = List.empty[String]
strList: List[String] = List()

scala> strList:+ "Text"
res3: List[String] = List(Text)

scala> val mapList = List.empty[Map[String, Any]]
mapList: List[Map[String,Any]] = List()

scala> mapList :+ Map("1" -> "ok")
res4: List[Map[String,Any]] = List(Map(1 -> ok))
Gihanchanuka
fuente
0

Tal vez pueda usar ListBuffers en scala para crear una lista vacía y agregar cadenas más tarde porque ListBuffers son mutables. Además, todas las funciones de lista están disponibles para ListBuffers en scala.

import scala.collection.mutable.ListBuffer 

val dm = ListBuffer[String]()
dm: scala.collection.mutable.ListBuffer[String] = ListBuffer()
dm += "text1"
dm += "text2"
dm = ListBuffer(text1, text2)

si lo desea, puede convertir esto en una lista usando .toList

Chalith Tharuka
fuente
0

En tu caso utilizo: val dm = ListBuffer[String]()yval dk = ListBuffer[Map[String,anyRef]]()

ROM
fuente