Есть ли способ построить HashSet с функцией инициализации в Котлин?

Чтобы прочитать «Звезд» из файла в задаче « Бумеранг» в блоге «Хакерский кубок» в 2016 году , можно определить следующую функцию расширения:

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

Код компактен, но значения сначала считываются в массив, а затем преобразуются в HashSet . Есть ли способ напрямую инициализировать HashSet с размером n и функцией инициализатора в Kotlin?

ОБНОВЛЕНИЕ: существует ли существующий способ в стандартных библиотеках Kotlin?

Вы всегда можете использовать apply для инициализации объектов на месте:

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

Если это слишком неудобно, введите каждый раз каждый раз, напишите функцию расширения:

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

Применение:

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

Поскольку HashSet является классом java, вы можете инициализировать его только способом JDK.

Хотя во время работы Kotlin нет вспомогательного метода, его легко написать так:

 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 } 

Как отметил @miensol, инициализация HashSet ограничивается конструкторами, доступными JDK. Kotlin добавил функцию hashSetOf которая инициализирует пустой HashSet а затем добавляет к нему указанные элементы.

Чтобы избежать первого считывания значений в массив, вы можете использовать kotlin.Sequence , kotlin.Sequence «значения оцениваются лениво»:

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

Похоже, вы задаете XY-вопрос ( http://xyproblem.info/ ). Вы действительно хотите знать, как писать readStars самым эффективным способом, но вместо этого вы спрашиваете о HashSet . Я думаю, что @ mfulton26 также отвечает на ваш вопрос в зависимости от того, что задается.

Вот ответ на вопрос «как я могу написать это наиболее эффективным способом:

У вас есть два варианта. Во-первых, версия, которая автоматически закрывает поток в конце:

 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() } } 

Во-вторых, версия, которая не делает:

 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() } 

Ни одна из версий не создает массив, и они не делают копии данных. Они передают данные через последовательность, которая создает набор и заполняет его напрямую.

Другие примечания

Не нужно использовать split, если вы действительно обеспокоены распределением и производительностью. Просто используйте indexOf(char) и разделите строку самостоятельно с помощью substring .

Если вы выполните разделение, используйте split(char) not split(String) когда вы хотите разбить на char