Kotlins Liste fehlt "Hinzufügen", "Entfernen", Karte fehlt "Setzen" usw.?

197

In Java könnten wir Folgendes tun

public class TempClass {
    List<Integer> myList = null;
    void doSomething() {
        myList = new ArrayList<>();
        myList.add(10);
        myList.remove(10);
    }
}

Aber wenn wir es wie unten beschrieben direkt in Kotlin umschreiben

class TempClass {
    var myList: List<Int>? = null
    fun doSomething() {
        myList = ArrayList<Int>()
        myList!!.add(10)
        myList!!.remove(10)
    }
}

Ich habe den Fehler erhalten, dass meine Liste nicht gefunden wurde addund nicht removefunktioniert

Ich arbeite daran, es in ArrayList umzuwandeln, aber das ist seltsam, wenn man es umwandeln muss, während in Java kein Casting erforderlich ist. Und das macht den Zweck der abstrakten Klassenliste zunichte

class TempClass {
    var myList: List<Int>? = null
    fun doSomething() {
        myList = ArrayList<Int>()
        (myList!! as ArrayList).add(10)
        (myList!! as ArrayList).remove(10)
    }
}

Gibt es eine Möglichkeit für mich, List zu verwenden, ohne sie umwandeln zu müssen, wie dies in Java möglich ist?

Elye
quelle
1
Nur ein Kommentar, warum Sie nicht können myList = nullund später beim Hinzufügen ohne hinzufügen !!. Sie können dies überwinden, indem Sie das lateinitSchlüsselwort vor Ihrer Eigenschaft folgendermaßen verwenden: Auf lateinit var myList: List<Int>diese Weise müssen Sie die Liste nicht sofort initialisieren, sondern garantieren dem Compiler, dass Sie sie initialisieren, bevor Sie die Liste zum ersten Mal verwenden. Es ist eine reibungslosere Lösung, aber Sie als Entwickler sind dafür verantwortlich.
Darwind

Antworten:

351

Im Gegensatz zu vielen anderen Sprachen unterscheidet Kotlin zwischen veränderlichen und unveränderlichen Sammlungen (Listen, Mengen, Karten usw.). Die genaue Kontrolle darüber, wann Sammlungen bearbeitet werden können, ist hilfreich, um Fehler zu beseitigen und gute APIs zu entwerfen.

https://kotlinlang.org/docs/reference/collections.html

Sie müssen eine MutableListListe verwenden.

class TempClass {
    var myList: MutableList<Int> = mutableListOf<Int>()
    fun doSomething() {
        // myList = ArrayList<Int>() // initializer is redundant
        myList.add(10)
        myList.remove(10)
    }
}

MutableList<Int> = arrayListOf() sollte auch funktionieren.

Fulvio
quelle
4
Schöne und gut geschriebene Antwort. Ich wähle deine als Modellantwort aus, obwohl ich meine unten habe :)
Elye
6
Sie müssen die Liste nicht auf Null setzen, wenn Sie sie sofort initialisieren. Ich habe deine Antwort bearbeitet.
Kirill Rakhman
37

Definieren einer Listensammlung in Kotlin auf verschiedene Arten:

  • Unveränderliche Variable mit unveränderlicher (schreibgeschützter) Liste:

    val users: List<User> = listOf( User("Tom", 32), User("John", 64) )


  • Unveränderliche Variable mit veränderlicher Liste:

    val users: MutableList<User> = mutableListOf( User("Tom", 32), User("John", 64) )

    oder ohne Anfangswert - leere Liste und ohne expliziten Variablentyp:

    val users = mutableListOf<User>()
    //or
    val users = ArrayList<User>()
    
    • Sie können der Liste Elemente hinzufügen:
      • users.add(anohterUser) oder
      • users += anotherUser(unter der Haube ist es users.add(anohterUser))


  • Veränderbare Variable mit unveränderlicher Liste:

    var users: List<User> = listOf( User("Tom", 32), User("John", 64) )

    oder ohne Anfangswert - leere Liste und ohne expliziten Variablentyp:

    var users = emptyList<User>()
    • HINWEIS: Sie können der Liste * Elemente hinzufügen:
      • users += anotherUser - * Es erstellt eine neue ArrayList und weist sie zu users


  • Veränderbare Variable mit veränderlicher Liste:

    var users: MutableList<User> = mutableListOf( User("Tom", 32), User("John", 64) )

    oder ohne Anfangswert - leere Liste und ohne expliziten Variablentyp:

    var users = emptyList<User>().toMutableList()
    //or
    var users = ArrayList<User>()
    
    • HINWEIS: Sie können Elemente zur Liste hinzufügen:
      • users.add(anohterUser)
      • aber nicht mit users += anotherUser

        Fehler: Kotlin: Mehrdeutigkeit der Zuweisungsoperatoren:
        Spaß des öffentlichen Operators Collection.plus (Element: String): In kotlin.collections definierte Liste
        @InlineOnly öffentlicher Inline-Operator fun MutableCollection.plusAssign (Element: String): In kotlin.collections definierte Einheit


Siehe auch: https://kotlinlang.org/docs/reference/collections.html

Lukas
quelle
9

Stimmen Sie allen oben genannten Antworten zur Verwendung von MutableList zu, aber Sie können auch eine Liste hinzufügen / daraus entfernen und eine neue Liste wie unten erhalten.

val newListWithElement = existingList + listOf(element)
val newListMinusElement = existingList - listOf(element)

Oder

val newListWithElement = existingList.plus(element)
val newListMinusElement = existingList.minus(element)
Dinesh Salbe
quelle
8

Anscheinend ist die Standardliste von Kotlin unveränderlich. Um eine Liste zu haben, die sich ändern könnte, sollte man MutableList wie folgt verwenden

class TempClass {
    var myList: MutableList<Int>? = null
    fun doSomething() {
        myList = ArrayList<Int>()
        myList!!.add(10)
        myList!!.remove(10)
    }
}

Aktualisiert Dennoch ist es nicht empfehlenswert MutableList zu verwenden , es sei denn , für eine Liste , dass Sie wirklich ändern möchten. Bezieht sich auf https://hackernoon.com/read-only-collection-in-kotlin-leads-to-better-coding-40cdfa4c6359, um zu erfahren, wie die schreibgeschützte Sammlung eine bessere Codierung bietet.

Elye
quelle
Sie haben Recht mit der Verwendung MutableList, aber das Initialisieren mit nullfunktioniert nicht. Auf einem vom Typ nullbaren Empfänger sind nur sichere oder nicht null-bestätigte Anrufe zulässig MutableList<Int>.
Fulvio
Es funktioniert auf meiner Seite und es wurde kein Fehler gefunden. Ich muss es nicht initialisieren, es sei denn, ich brauche es, wenndoSomething
Elye
@Elye Ich weiß, dass dies eine alte Antwort und Frage ist, aber die Verwendung, lateinitanstatt die Liste auf Null zu setzen, ist der richtige Weg, um dieses Problem zu lösen . Ich erinnere mich nicht, ob lateinites von Anfang an zu Kotlin hinzugefügt wurde, aber dies ist heutzutage definitiv die Lösung, um es zu verwenden :-)
Darwind
5

In Kotlin müssen Sie MutableListoder verwenden ArrayList.

Mal sehen, wie die MutableListArbeitsmethoden funktionieren:

var listNumbers: MutableList<Int> = mutableListOf(10, 15, 20)
// Result: 10, 15, 20

listNumbers.add(1000)
// Result: 10, 15, 20, 1000

listNumbers.add(1, 250)
// Result: 10, 250, 15, 20, 1000

listNumbers.removeAt(0)
// Result: 250, 15, 20, 1000

listNumbers.remove(20)
// Result: 250, 15, 1000

for (i in listNumbers) { 
    println(i) 
}

Mal sehen, wie die ArrayListArbeitsmethoden funktionieren:

var arrayNumbers: ArrayList<Int> = arrayListOf(1, 2, 3, 4, 5)
// Result: 1, 2, 3, 4, 5

arrayNumbers.add(20)
// Result: 1, 2, 3, 4, 5, 20

arrayNumbers.remove(1)
// Result: 2, 3, 4, 5, 20

arrayNumbers.clear()
// Result: Empty

for (j in arrayNumbers) { 
    println(j) 
}
Andy
quelle
4

Sie können so ein neues erstellen.

var list1 = ArrayList<Int>()
var list2  = list1.toMutableList()
list2.add(item)

Jetzt können Sie list2 verwenden. Danke.

Dalvinder Singh
quelle
2

Beschränkung der Mutabilität auf Builder

Die Top-Antworten hier sprechen korrekt für den Unterschied in Kotlin zwischen schreibgeschützt List(HINWEIS: schreibgeschützt, nicht "unveränderlich" ) und MutableList.

Im Allgemeinen sollte man sich bemühen, schreibgeschützte Listen zu verwenden. Die Veränderbarkeit ist jedoch zur Konstruktionszeit häufig noch nützlich , insbesondere wenn es sich um Bibliotheken von Drittanbietern mit nicht funktionierenden Schnittstellen handelt. In Fällen, in denen alternative Konstruktionstechniken nicht verfügbar sind, z. B. die listOfdirekte Verwendung oder Anwendung eines funktionalen Konstrukts wie foldoder reduce, erstellt ein einfaches Konstruktor mit "Builder-Funktion" wie das folgende eine schreibgeschützte Liste aus einer temporär veränderlichen:

val readonlyList = mutableListOf<...>().apply {
  // manipulate your list here using whatever logic you need
  // the `apply` function sets `this` to the `MutableList`
  add(foo1)
  addAll(foos)
  // etc.
}.toList()

und dies kann gut in eine wiederverwendbare Inline-Dienstprogrammfunktion eingekapselt werden:

inline fun <T> buildList(block: MutableList<T>.() -> Unit) = 
  mutableListOf<T>().apply(block).toList()

was so genannt werden kann:

val readonlyList = buildList<String> {
  add("foo")
  add("bar")
}

Jetzt ist die gesamte Veränderlichkeit auf einen Blockbereich beschränkt, der für die Erstellung der schreibgeschützten Liste verwendet wird, und der Rest Ihres Codes verwendet die schreibgeschützte Liste, die vom Builder ausgegeben wird.

UPDATE : Ab Kotlin 1.3.70 ist die oben genannte genaue buildListFunktion in der Standardbibliothek als experimentelle Funktion zusammen mit ihren Analoga und verfügbar . Siehe https://blog.jetbrains.com/kotlin/2020/03/kotlin-1-3-70-released/ .buildSetbuildMap

Raman
quelle
Vielen Dank und gut mit der Geschäftslogik innerhalb der Apply-Methode gearbeitet, sogar anotherList.ForEach {add (foo)} in der .appy {}
BENN1TH
1

Im Konzept unveränderlicher Daten ist dies möglicherweise ein besserer Weg:

class TempClass {
    val list: List<Int> by lazy {
        listOf<Int>()
    }
    fun doSomething() {
        list += 10
        list -= 10
    }
}
bbsimon
quelle
1

A listist immutabledurch Default, können Sie ArrayListstattdessen. so was :

 val orders = arrayListOf<String>()

dann können Sie add/deleteArtikel davon wie folgt:

orders.add("Item 1")
orders.add("Item 2")

standardmäßig ArrayListist , mutableso dass Sie die Operationen darauf ausführen können.

Donner
quelle