Jak zadeklarować pustą listę, a następnie dodać ciąg w scali?

81

Mam taki kod:

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

.....

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

ale generuje java.lang.UnsupportedOperationException środowiska wykonawczego.

Muszę zadeklarować pustą listę lub puste mapy, a niektóre miejsca w kodzie muszą je wypełnić.

rjc
źródło
Co sprawia, że ​​myślisz, że jest addoperacja List?
Dębilski
Jeśli chcesz użyć operacji dodawania, musisz zadeklarować ArrayList. Wartości w scali są zasadniczo niezmienne, więc nie można ich dodawać.
Phantom73
1
iirc val jest bardziej jak wersja ostateczna, możesz do nich dodać, jeśli używasz mutowalnych kolekcji. np. scala-lang.org/api/current/scala/collection/mutable/…
DaVinci
1
@rjc Której wersji scali używasz? Mój (2.9.0) daje mi błąd kompilacji.
paradygmatyczny
4
Czy importowałeś scala.collection.JavaConversions? Jeśli tak, widzisz powód, dla którego zalecam JavaConverterszamiast tego: dmi dksą konwertowane na kolekcję Java, a następnie addmetodę wywoływaną w tej kolekcji. Gorzej dmi dknie są modyfikowane, nawet jeśli nie pojawił się błąd. I, przy okazji, błąd jest to, że 1 -> "ok"jest Map[Int,String], nie Map[String, Object].
Daniel C. Sobral

Odpowiedzi:

117

Listy Scala są domyślnie niezmienne. Nie możesz „dodać” elementu, ale możesz utworzyć nową listę, dołączając nowy element na początku. Ponieważ jest to nowa lista, musisz ponownie przypisać odniesienie (więc nie możesz użyć wartości).

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

.....

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

Operator ::tworzy nową listę. Możesz także użyć krótszej składni:

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

NB: W SCALA nie użyć typu Objectale Any, AnyRefalbo AnyVal.

paradygmatyczny
źródło
Bardzo dobra odpowiedź, ale czy możesz stwierdzić, czy deklaruję listę jak w Twojej odpowiedzi, czy są to typy scala.collections.mutable czy immutable? REPL nie wyjaśnił tego jasno.
rjc
2
Domyślnie. Jeśli nic nie importujesz. Listjest niezmienna. To zalecany do większości zastosowań.
paradygmatyczny
11
@rjc Scala nie ma mutable.List- Listjest typem konkretnym, którego jedyna implementacja jest niezmienna. Istnieją niezmienne klasy, takie jak LinkedListi DoubleLinkedList, które są w większości klasami pomocniczymi. Odpowiednikiem Javy w Scali ArrayListjest ArrayBuffer, a odpowiednikiem Javy LinkedListjest ListBuffer. Cechą, która odpowiada Javie, Listjest Seq- której istnieje collection.Seqi, rozszerzając ją, collection.immutable.Seqi collection.mutable.Seq.
Daniel C. Sobral
@paradigmatic czy istnieje różnica między ::=i +=?
Mahdi,
@Mahdi Może być różnica. Tylko na listach ::jest zdefiniowane, więc +=nie będzie działać. W innej kolekcji (nie w standardowej bibliotece lib): jeśli implementacja ::=lub +=jest zaimplementowana, zostanie użyta. W przeciwnym razie kompilator zamieni się x::=yw x = y::xi x+=yinro x=x+y. W drugim przypadku są takie same, jeśli realizacja ::jest taka sama jak realizacja +...
paradygmatyczna
17

Jeśli chcesz zmutować coś, użyj ArrayBufferlub LinkedBufferzamiast tego. Jednak lepiej byłoby odnieść się do tego stwierdzenia:

Muszę zadeklarować pustą listę lub puste mapy, a niektóre miejsca w kodzie muszą je wypełnić.

Zamiast tego wypełnić listę kodem, który zwraca elementy. Jest na to wiele sposobów i podam kilka przykładów:

// 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
źródło
14

Jak wszyscy już wspominali, nie jest to najlepszy sposób korzystania z list w Scali ...

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
źródło
Czy możesz stwierdzić, czy Twoja lista jest zadeklarowana tak, jak w Twojej odpowiedzi, czy zapewni ona lepszą wydajność w czasie wykonywania w porównaniu z odpowiedzią paradygmetyką? Załóżmy, że do listy zostaną dodane miliony elementów.
rjc,
To zależy od tego, co próbujesz osiągnąć. Poleciłbym zacząć od niezmiennego, jak zasugerował @paradigmatic. Złożoność dodawania elementu do niezmiennej listy, taka jak ta: list ::= "text"to O (1), które jest stałe i najlepsze, co możesz zrobić.
agilesteel
rjc: minusy niezmiennych list to O (1); Jednak to, co naprawdę ma znaczenie, to wzorzec dostępu, jeśli chodzi o wydajność. Na przykład, jeśli kolejność ma znaczenie i musisz utworzyć listę przez dołączanie, lepszym (niezmiennym) wyborem jest Vector.
Kris Nuttycombe
6

Jak wspomniano w powyższej odpowiedzi , lista Scala jest niezmienną kolekcją. Możesz utworzyć pustą listę za pomocą .empty[A]. Następnie można użyć metody :+, +:lub ::w celu dodania elementu do listy.

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
źródło
0

Może możesz użyć ListBuffers w scali, aby utworzyć pustą listę i dodać ciągi później, ponieważ ListBuffers są mutowalne. Również wszystkie funkcje List są dostępne dla ListBuffers w scali.

import scala.collection.mutable.ListBuffer 

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

jeśli chcesz, możesz przekonwertować to na listę za pomocą .toList

Chalith Tharuka
źródło
0

W twoim przypadku używam: val dm = ListBuffer[String]()ival dk = ListBuffer[Map[String,anyRef]]()

Rom
źródło