Я хочу, чтобы мелкий скопировать список в Scala.
Я хотел сделать что-то вроде:
val myList = List("foo", "bar")
val myListCopy = myList.clone
Но метод clone защищен.
Я хочу, чтобы мелкий скопировать список в Scala.
Я хотел сделать что-то вроде:
val myList = List("foo", "bar")
val myListCopy = myList.clone
Но метод clone защищен.
Чтобы отфильтровать список:
val list = List(1,2,3,4,5)
//only evens
val evens = list.filter(e=>e%2 == 0)
println(list)
//--> List(1, 2, 3, 4, 5)
println(evens)
//--> List(2, 4)
Вы также можете использовать подстановочный знак для сохранения нескольких символов:
val evens = list.filter(_%2==0)
Обратите внимание, что, как отмечалось выше, списки являются неизменными. Это означает, что эти операции не изменяют исходный список, а фактически создают новый список.
Здесь нет ответа: не делайте этого. A List
является неизменным, поэтому нет абсолютно никакого смысла копировать его.
Рассмотрим несколько операций:
val list = List(1,2,3)
val l1 = 0 :: list
val l2 = "a" :: list
Ни l1
, ни l2
не изменяют List
, но оба создают новые списки, ссылающиеся на List
.
Объясним это подробно. Конструктор List(1,2,3)
создает три элемента и использует один и тот же объект. В частности, он создает эти элементы:
::(3, Nil)
::(2, reference to the previous element)
::(1, reference to the previous element)
И Nil
- одноэлементный объект. То, что на самом деле указывает идентификатор List
, это последний элемент.
Теперь, когда вы назначаете 0 :: list
на l1
, вы создаете экземпляр одного нового объекта:
::(0, reference to ::(1, etc))
Конечно, поскольку есть ссылка на List
, вы можете представить l1
как список из четырех элементов (или пять, если вы считаете Nil
).
Теперь l2
даже не того же типа List
, но он также ссылается на него! Здесь:
::("a", reference to ::(1, etc))
Важным моментом во всех этих объектах является то, что они не могут быть изменены. Нет никаких сеттеров, ни каких-либо методов, которые бы изменили любые их свойства. Они будут навсегда иметь одинаковые значения/ссылку в своей "голове" (то, что мы называем первым элементом), и те же ссылки в их "хвосте" (что мы называем вторым элементом).
Однако есть методы, которые выглядят так, как будто они меняют список. Будьте уверены, однако, что они создают новые списки. Например:
val l3 = list map (n => n + 1)
Карта метода создает совершенно новый список того же размера, где новый элемент может быть вычислен из соответствующего элемента в List
(но вы также можете игнорировать старый элемент).
val l4 = l2 filter (n => n.isInstanceOf[Int])
Хотя l4
имеет те же элементы, что и List
(но другой тип), это также совершенно новый список. Метод filter
создает новый список, основанный на правиле, которое вы передаете, чтобы сообщить ему, какие элементы входят, а какие нет. Он не пытается оптимизировать, если он может вернуть существующий список.
val l5 = list.tail
Это не создает новый список. Вместо этого он просто присваивает l5
существующий элемент List
.
val l6 = list drop 2
Снова не создается новый список.
val l7 = list take 1
Это, однако, создает новый список именно потому, что он не может изменить первый элемент List
так, чтобы его хвост указывал на Nil
.
Вот несколько дополнительных деталей реализации:
List
- абстрактный класс. Он имеет два потомка, класс ::
(да, это имя класса) и одноэлементный объект Nil
. List
запечатан, поэтому вы не можете добавлять к нему новые подклассы, а ::
является окончательным, поэтому вы не можете подклассифицировать его.
Пока вы ничего не можете изменить для изменения списка, он использует изменчивое состояние внутри некоторых операций. Это помогает в производительности, но локализовано таким образом, что никакая программа, которую вы пишете, никогда не сможет ее обнаружить или получить от нее последствия. Вы можете передавать списки по своему усмотрению, независимо от того, что с ними делают другие функции, или сколько потоков использует их одновременно.