Scala.collection.breakOut vs views
Этот ответ SO описывает, как scala.collection.breakOut
можно использовать для предотвращения создания расточительных промежуточных коллекций. Например, здесь мы создаем промежуточный Seq[(String,String)]
:
val m = List("A", "B", "C").map(x => x -> x).toMap
Используя breakOut
, мы можем предотвратить создание этого промежуточного Seq
:
val m: Map[String,String] = List("A", "B", "C").map(x => x -> x)(breakOut)
Views решает ту же проблему и, кроме того, элементы доступа лениво:
val m = (List("A", "B", "C").view map (x => x -> x)).toMap
Я предполагаю, что создание оберток View
довольно дешево, поэтому мой вопрос: есть ли настоящая причина использовать breakOut
над View
s?
Ответы
Ответ 1
Я не думаю, что views
и breakOut
идентичны.
A breakOut
- это реализация CanBuildFrom
, используемая для упрощения операций преобразования путем исключения промежуточных шагов. Например, получить от А до Б без посреднической коллекции. A breakOut
означает, что Scala выберите соответствующий объект-строитель для максимальной эффективности создания новых элементов в данном сценарии. Подробнее здесь.
views
имеют дело с другим типом эффективности, основным этапом продажи является: "Больше нет новых объектов". В представлениях хранятся световые ссылки на объекты для решения различных сценариев использования: ленивый доступ и т.д.
Нижняя строка:
Если вы map
на view
, вы все равно можете получить промежуточную коллекцию ссылок, созданных до того, как будет получен ожидаемый результат. У вас все еще может быть превосходная производительность:
collection.view.map(somefn)(breakOut)
Чем от:
collection.view.map(someFn)
Ответ 2
Вы собираетесь совершить поездку из Англии во Францию.
С просмотром: вы берете набор заметок в своем ноутбуке и бума, как только вы вызвали .force(), вы начинаете создавать все из них: buy a ticket, board on the plane, ....
С breakOut: вы уходите и бумете, вы в Париже, глядя на Эйфелеву башню. Вы не помните, как именно вы прибыли туда, но вы действительно совершили эту поездку, просто не сделали никаких воспоминаний.
Плохая аналогия, но я надеюсь, что это даст вам представление о том, в чем разница между ними.
Ответ 3
Что сказал флавиан.
Одним из вариантов использования представлений является сохранение памяти. Например, если у вас была строка длиной в миллион символов original
и ей нужно было использовать один за другим все миллионы суффиксов этой строки, вы можете использовать коллекцию
val v = original.view
val suffixes = v.tails
представления исходной строки. Затем вы можете циклически перебирать суффиксы один за другим, используя suffix.force()
, чтобы преобразовать их обратно в строки в цикле, таким образом, только удерживая один в памяти за раз. Конечно, вы могли бы сделать то же самое, перебирая свой собственный цикл по индексам исходной строки, а не создавая какую-либо коллекцию суффиксов.
Другой вариант использования - когда создание производных объектов дорогое, вам нужны они в коллекции (например, как значения на карте), но вы только получите доступ к нескольким, и вы не знаете, какие из них.
Если у вас действительно есть случай, когда выбор между ними имеет смысл, предпочитайте breakOut, если нет хорошего аргумента для использования представления (например, выше).
- Для просмотра требуется больше изменений и ухода кода, чем breakOut, в том случае, если вам нужно добавить force(), где это необходимо. В зависимости от контекста невозможность сделать это
часто обнаруживается только во время выполнения. С breakOut, как правило, если это
компилирует, это правильно.
- В случаях, когда представление не применяется, breakOut
будет быстрее, так как генерация представления и форсирование пропускаются.
- Если вы используете отладчик, вы можете проверить содержимое коллекции, которое вы
не может осмысленно делать с коллекцией представлений.