4.5. Основы Kotlin. Хранение данных в памяти компьютера

Значения и ссылки

В Котлине существует два способа хранения переменных (параметров) в памяти JVM: хранение значений и хранение ссылок. В любом из этих способов для переменной выделяется ячейка памяти, размер которой зависит от типа переменной, но не превышает 8 байт.

При хранении значений в эту ячейку помещается значение переменной — так обычно (строго говоря, не всегда) происходит с переменными целочисленного, вещественного и символьного типа. При изменении значения переменной изменяется и содержимое соответствующей ей ячейки.

При хранении ссылок в ячейку переменной помещается ссылка, при этом значение (содержимое) переменной хранится в специальном участке памяти JVM — куче (heap). Каждому используемому участку памяти кучи соответствует определённый номер, и как раз этот номер и используется в качестве ссылки. То есть, при хранении ссылок для чтения значения переменной необходимо выполнить не одно, а два действия:

  • прочитать номер участка в куче из ячейки переменной;
  • по этому номеру обратиться к куче и прочитать значение переменной.

Хранение ссылок используется для всех составных и нестандартных типов, в частности, для строк, массивов, списков. При изменении переменной в результате выполнения оператора вроде v = …​ изменяется ссылка. Например:

Обратите внимание, что после выполнения трёх приведённых операторов в участке кучи с номером 2 хранится список [4, 5], но ни одна переменная не хранит ссылку на этот участок. Подобный участок через некоторое время будет найден и уничтожен специальной программой JVM — сборщиком мусора, он же Garbage Collector.

Такие типы, как String или List, не предполагают возможность изменения содержимого переменной. Опять-таки при попытке выполнить оператор вида s = …​ изменится ссылка. Например:

При сложении a и b будет создана новая строка AlphaBeta и размещена в участке памяти с номером 3. После этого номер 3 будет записан в переменную b. Отметьте, что c по-прежнему хранит номер 2, а a — номер 1.

Особенно интересна ситуация с типом MutableList, который позволяет изменять и содержимое переменной тоже. Например:

После выполнения оператора b[2] = 5 участок памяти с номером 1 будет хранить список [1, 2, 5]. Поскольку в переменной a хранится тот же номер 1, то вывод на консоль a[2] приведёт к выводу числа 5, хотя раньше этот элемент списка хранил значение 3.

Подобный принцип используют и функции, имеющие параметр с типом MutableList:

При вызове invertPositives номер 1 будет переписан из аргумента a в параметр list. После этого функция invertPositives изменит содержимое списка, используя данный номер, и вызов println(a) выведет [-1, -2, -3] на консоль.

Таким образом, имея дело с типами, хранящимися по ссылке (чаще говорят проще — ссылочные типы), стоит различать действия со ссылками и действия со значениями. К примеру, присваивание name = …​ — это всегда действие со ссылкой. С другой стороны, вызов функции вроде list.isEmpty() или индексация вроде list[i]list[j] = i — это действия с содержимым, причём, некоторые из этих действий только читают содержимое переменной, а некоторые другие — изменяют его.

С учётом этого различия в Котлине определено две разных операции сравнения на равенство: уже известная нам ==и новая ===. Операция a == b — это сравнение содержимого на равенство, которое обычно выполняется с помощью вызова функции a.equals(b) — про неё мы поговорим в разделе 9. Операция a === b — это сравнение ссылок на равенство, для которого не имеет значения, одинаковое содержимое у переменных или нет, важно только, чтобы оно находилось в участке кучи с одинаковым номером. Например:

Здесь a и b имеют одно и то же содержимое, но находятся в участках кучи с разными номерами. Операция !=обратна операции == (сравнение содержимого на неравенство), а операция !==, соответственно — обратна операции === (сравнение ссылок на неравенство).

Важно также, что сравнение содержимого на равенство не реализовано для массивов Array, и поэтому для них операции == и === эквивалентны. Это одна из причин, по которой следует использовать списки вместо массивов, где это возможно. Пример:

Добавить комментарий