Frage Gibt es eine Möglichkeit, ein HashSet mit Initialisierungsfunktion in Kotlin zu erstellen?


Sterne aus einer Datei in der Facebook Hacker Cup 2016 Boomerang Constellations Problem, folgende Erweiterungsfunktion kann definiert werden:

fun BufferedReader.readStars(n: Int): Set<Star> {
    return Array(n) {
        val (l1, l2) = readLine().split(" ").map { it.toInt() }
        Star(l1, l2)
    }.toHashSet()
}

Der Code ist kompakt, aber die Werte werden zuerst in ein Array eingelesen und dann in a konvertiert HashSet. Gibt es eine Möglichkeit, eine direkt zu initialisieren? HashSet mit der Größe von n und Initiatorfunktion in Kotlin?

AKTUALISIEREN: Ist dort ein bestehende Weg in Standard-Kotlin-Bibliotheken?


5
2018-01-12 08:25


Ursprung


Antworten:


Sie können immer verwenden apply Objekte direkt initialisieren:

HashSet<Star>(n).apply {
    repeat(n) {
        val (l1, l2) = readLine()!!.split(' ').map { it.toInt() }
        put(Star(l1, l2))
    }
}

Wenn das zu unbequem ist, geben Sie jedes Mal eine Erweiterungsfunktion ein:

inline fun <T> createHashSet(n : Int, crossinline fn: (Int) -> T) = HashSet<T>(n).apply {
    repeat(n) { add(fn(it)) }
}

Verwendung:

createHashSet<Star>(n) {
    val (l1, l2) = readLine()!!.split(' ').map { it.toInt() }
    Star(l1, l2)
}

7
2018-01-12 09:25



Schon seit HashSet ist eine Java-Klasse, so dass Sie sie nur auf eine Weise initialisieren können, die von JDK bereitgestellt wird.

Solange da ist keine Hilfsmethode in Kotlin Runtime es ist einfach, es selbst so zu schreiben:

public fun <T> hashSetOf(size: Int, initializer: (Int) -> T): HashSet<T> {
    val result = HashSet<T>(size)
    0.rangeTo(size - 1).forEach {
        result.add(initializer(it))
    }
    return result
}

5
2018-01-12 09:02



Wie @miensol darauf hingewiesen hat HashSet Die Initialisierung ist auf die vom JDK bereitgestellten Konstruktoren beschränkt. Kotlin hat hinzugefügt hashSetOf Funktion, die ein leeres initialisiert HashSet und fügt dann die angegebenen Elemente hinzu.

Um das erste Lesen der Werte in ein Array zu vermeiden, können Sie a verwenden kotlin.Sequence Wer ist "Werte werden faul bewertet":

fun BufferedReader.readStars(n: Int): Set<Star> {
    return lineSequence().take(n).map {
        val (l1, l2) = it.split(" ").map { it.toInt() }
        Star(l1, l2)
    }.toHashSet()
}

1
2018-01-12 14:43



Es scheint, als ob Sie eine XY-Frage stellen (http://xyproblem.info/). Sie möchten wirklich wissen, wie man schreibt readStars auf die effizienteste Weise, aber stattdessen fragen Sie nach HashSet. Ich denke @ mfulton26 beantwortet auch Ihre Frage, je nachdem, was gefragt wird.

Hier ist die Antwort für "wie schreibe ich das auf die effizienteste Weise:"

Sie haben zwei Möglichkeiten. Erstens, eine Version, die den Stream am Ende automatisch schließt:

fun BufferedReader.readStars(n: Int): Set<Star> {
    return use {
        lineSequence().map { line ->
            val idx = line.indexOf(' ')
            Star(line.substring(0, idx).toInt(), line.substring(idx + 1).toInt())
        }.toSet()
    }
}

Und zweitens, eine Version, die nicht:

fun BufferedReader.readStars(n: Int): Set<Star> {
    return lineSequence().map { line ->
            val idx = line.indexOf(' ')
            Star(line.substring(0, idx).toInt(), line.substring(idx+1).toInt())
        }.toSet()
}

Keine der beiden Versionen erstellt ein Array und auch keine Kopien von Daten. Sie streamen die Daten durch eine Sequenz, die das Set erzeugt und direkt füllt.

Weitere Hinweise

Sie müssen Split nicht verwenden, wenn Sie wirklich Bedenken hinsichtlich der Zuweisung und Leistung haben. Benutz einfach indexOf(char) und spalte die Saite selbst mit substring.

Wenn Sie einen Split machen, dann benutzen Sie bitte split(char) nicht split(String) wenn Sie schauen, um sich auf einem zu teilen char


1
2018-01-12 15:41