Почему Java 8 не содержит неизменяемых коллекций?

Команда Java проделала огромную работу, устраняя барьеры для функционального программирования на Java 8. В частности, изменения в java.util Collections делают отличную работу по преобразованию цепочек в очень быстрые потоковые операции. Учитывая, насколько хороша работа, которую они сделали, добавляя функции первого класса и функциональные методы в коллекциях, почему они полностью не смогли обеспечить неизменяемые коллекции или даже неизменные интерфейсы коллекции?

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

                  ImmutableIterable
     ____________/       |
    /                    |
Iterable        ImmutableCollection
   |    _______/    /          \   \___________
   |   /           /            \              \
 Collection  ImmutableList  ImmutableSet  ImmutableMap  ...
    \  \  \_________|______________|__________   |
     \  \___________|____________  |          \  |
      \___________  |            \ |           \ |
                  List            Set           Map ...

Конечно, операции, подобные List.add () и Map.put (), в настоящее время возвращают логическое или предыдущее значение для данного ключа, чтобы указать, была ли операция успешной или неудачной. Неизменяемые коллекции должны были бы рассматривать такие методы, как фабрики, и возвращать новую коллекцию, содержащую добавленный элемент, - который несовместим с текущей подписью. Но это можно было бы обработать с помощью другого имени метода, такого как ImmutableList.append () или .addAt () и ImmutableMap.putEntry (). Результирующая многословие будет более чем перевешиваться преимуществами работы с неизменяемыми коллекциями, а система типов предотвратит ошибки при вызове неправильного метода. Со временем старые методы могут быть устаревшими.

Побеждает неизменяемых коллекций:

  • Простота - рассуждение о коде проще, когда базовые данные не изменяются.
  • Документация - если метод принимает неизменный интерфейс коллекции, вы знаете, что он не собирается изменять эту коллекцию. Если метод возвращает неизменяемую коллекцию, вы знаете, что не можете ее изменить.
  • Параллелизм - неизменяемые коллекции можно безопасно распределять между потоками.

Как человек, который попробовал языки, которые предполагают неизменность, очень трудно вернуться на Дикий Запад от безудержной мутации. В коллекциях Clojure (абстракция последовательности) уже есть все, что предоставляют коллекции Java 8, а также неизменяемость (хотя, возможно, использование дополнительной памяти и времени из-за синхронизированных связанных списков вместо потоков). Scala имеет как изменяемые, так и неизменные коллекции с полным набором операций, и хотя эти операции очень интересны, вызов .iterator дает ленивое представление (и есть другие способы ленивой оценки их). Я не вижу, как Java может продолжать конкурировать без неизменных коллекций.

Может кто-нибудь указать мне на историю или дискуссию об этом? Наверняка это публично.

121 голос | спросил GlenPeterson 18 WedEurope/Moscow2013-12-18T18:53:54+04:00Europe/Moscow12bEurope/MoscowWed, 18 Dec 2013 18:53:54 +0400 2013, 18:53:54

6 ответов


106

Поскольку неизменяемые коллекции абсолютно требуют совместного использования. В противном случае всякая операция приводит к тому, что в кучу попадает целый список. Языки, которые полностью неизменяемы, например Haskell, создают удивительное количество мусора без агрессивной оптимизации и совместного использования. Наличие коллекции, которая используется только с <50 элементами, не стоит вставлять в стандартную библиотеку.

Кроме того, неизменные коллекции часто имеют принципиально разные реализации, чем их изменчивые коллеги. Рассмотрим, например, ArrayList, эффективный неизменяемый ArrayList не будет массивом вообще! Он должен быть реализован с сбалансированным деревом с большим коэффициентом ветвления, Clojure использует 32 IIRC. Создание изменчивых коллекций будет «неизменным», просто добавив функциональное обновление, это ошибка производительности, равно как и утечка памяти.

Кроме того, совместное использование в Java небезопасно. Java предоставляет слишком много неограниченных привязок к изменчивости и равенству ссылок, чтобы сделать обмен «просто оптимизацией». Вероятно, это немного раздражает вас, если вы можете изменить элемент в списке и понять, что вы только что изменили элемент в других 20 версиях этого списка.

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

ответил jozefg 18 WedEurope/Moscow2013-12-18T20:38:47+04:00Europe/Moscow12bEurope/MoscowWed, 18 Dec 2013 20:38:47 +0400 2013, 20:38:47
64

Измененная коллекция не является подтипом неизменяемой коллекции. Вместо этого изменяемые и неизменные коллекции являются потомками потомков читаемых коллекций. К сожалению, понятия «читаемые», «только для чтения» и «неизменные» кажутся размытыми вместе, хотя они означают три разные вещи.

  • Читаемый базовый класс коллекции или тип интерфейса обещает, что можно читать элементы и не предоставляет никаких прямых средств для изменения коллекции, но не гарантирует, что код, получающий ссылку, не может отличать или манипулировать им в таком чтобы разрешить модификацию.

  • Интерфейс коллекции только для чтения не включает никаких новых членов, но должен быть реализован только классом, который обещает, что нет способа манипулировать ссылкой на него таким образом, чтобы мутировать коллекцию ни получить ссылку на то, что может это сделать. Однако он не обещает, что коллекция не будет модифицирована чем-то другим, имеющим ссылку на внутренности. Обратите внимание, что интерфейс коллекции только для чтения может быть не в состоянии предотвратить реализацию изменчивыми классами, но может указывать, что любая любая реализация или класс, полученный из реализации, которая допускает мутацию, считается «незаконной» реализацией или производной от реализации .

  • Неизменяемая коллекция - это та, которая всегда будет содержать одни и те же данные, если существует какая-либо ссылка на нее. Любая реализация неизменяемого интерфейса, которая не всегда возвращает одни и те же данные в ответ на конкретный запрос, нарушена.

Иногда бывает полезно иметь сильно связанные мутируемые и неизменные типы коллекций, которые как реализуются, так и производятся из одного и того же «читаемого» типа, а также для того, чтобы читать читаемый тип: AsImmutable, AsMutable и AsNewMutable. Такая конструкция может позволить код, который хочет сохранить данные в коллекции, для вызова AsImmutable; этот метод сделает защитную копию, если коллекция изменена, но пропустите копию, если она уже неизменяема.

ответил supercat 21 SatEurope/Moscow2013-12-21T12:37:45+04:00Europe/Moscow12bEurope/MoscowSat, 21 Dec 2013 12:37:45 +0400 2013, 12:37:45
13

Структура коллекций Java предоставляет возможность создавать доступную для чтения версию коллекции с помощью шести статических методов в класс java.util.Collections :

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

Однако эта проблема осталась бы вне зависимости от того, возвращает ли код один объект или немодифицируемый набор объектов. Если тип позволяет мутировать его объекты, то это решение было принято в дизайне типа, и я не вижу, как это может изменить изменение JCF. Если важна неизменность, то члены коллекции должны иметь неизменный тип.

ответил Arkanon 26 ThuEurope/Moscow2013-12-26T01:44:16+04:00Europe/Moscow12bEurope/MoscowThu, 26 Dec 2013 01:44:16 +0400 2013, 01:44:16
7

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

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

И я не могу винить их: интерфейс, который притворяется, что он читает-пишет, но бросает UnsupportedOperationException, если вы совершаете ошибку при вызове любого из своих методов «write», является довольно злым вещь, чтобы иметь!

Теперь интерфейс, такой как Collection, который будет отсутствовать add(), remove() и clear() не будет «интерфейсом ImmutableCollection»; это был бы интерфейс UnmodifiableCollection. На самом деле никогда не может быть интерфейса «ImmutableCollection», потому что неизменяемость является природой реализации, а не характеристикой интерфейса. Я знаю, это не очень понятно; позвольте мне объяснить.

Предположим, что кто-то вручает вам такой интерфейс для чтения только для чтения; безопасно ли передавать его в другой поток? Если бы вы точно знали, что он представляет собой действительно неизменяемую коллекцию, тогда ответ будет «да»; к сожалению, поскольку это интерфейс, вы не знаете, как он реализован, поэтому ответ должен быть no : для всего, что вы знаете, это может быть немодифицируемым (вам) представление коллекции, которая на самом деле изменена (например, что вы получаете с помощью Collections.unmodifiableCollection()), поэтому попытка чтения с нее, пока другой поток изменяет, приведет к чтению поврежденные данные.

Итак, то, что вы по существу описали, представляет собой набор неинтегрируемых интерфейсов коллекции «Неизменяемый», но «Unmodifiable». Важно понимать, что «Unmodifiable» просто означает, что тот, кто имеет ссылку на такой интерфейс, не может изменять базовую коллекцию, и их предотвращают просто потому, что интерфейс не имеет каких-либо методов модификации, а не потому, что базовая коллекция обязательно непреложна. Основная коллекция может быть очень изменчивой; вы не знаете и не контролируете это.

Чтобы иметь неизменяемые коллекции, они должны быть classes , а не интерфейсами!

Эти непреложные классы коллекций должны быть окончательными, так что, когда вам дается ссылка на такую ​​коллекцию, вы точно знаете, что она будет вести себя как непреложная коллекция независимо от того, что вы, или кто-либо еще, кто имеет ссылку на он может сделать с ним.

Итак, чтобы иметь набор полных наборов в java (или любой другой декларативный императивный язык), нам понадобится следующее:

  1. Набор немодифицируемых коллекций интерфейсов .

  2. Набор изменяемых коллекций интерфейсов , расширяющих неизменяемые.

  3. Набор изменяемых коллекций классов , реализующих изменяемые интерфейсы, а также расширение немодифицируемых интерфейсов.

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

Я использовал все вышеперечисленное для удовольствия, и я использую их в проектах, и они работают как обаяние.

Причина, по которой они не являются частью среды java, вероятно, связана с тем, что считалось, что это слишком сложно /слишком сложно /слишком сложно понять.

Лично я думаю, что то, что яописанное выше, недостаточно даже; еще одна вещь, которая, по-видимому, необходима, - это набор изменяемых интерфейсов & классы для структурной неизменности . (Который может просто называться «Жесткий», потому что префикс «StructurallyImmutable» слишком длинный.)

ответил Mike Nakis 4 J0000006Europe/Moscow 2015, 18:07:35
2

Неизменяемые коллекции могут быть глубоко рекурсивными, по сравнению с eachother, и не необоснованно неэффективны, если равенство объектов - secureHash. Это называется лесом мерок. Он может быть на коллекцию или в некоторых частях, например, (самобалансирующееся двоичное) дерево AVL для сортированной карты.

Если все объекты java в этих коллекциях не имеют уникального идентификатора или некоторой битовой строки для хэша, коллекция не имеет ничего, чтобы однозначно назвать себя.

Пример. На моем ноутбуке 4x1.6ghz я могу запустить 200K sha256s в секунду наименьшего размера, который соответствует 1 хэш-циклу (до 55 байт), по сравнению с 500K операциями HashMap или 3M ops в хэш-таблице длин. 200K /log (collectionSize) новые коллекции в секунду достаточно быстро для некоторых вещей, где важна целостность данных и анонимная глобальная масштабируемость.

ответил Ben Rayfield 4 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowSun, 04 Sep 2016 22:52:01 +0300 2016, 22:52:01
-3

Производительность. Коллекции по своей природе могут быть очень большими. Копирование 1000 элементов в новую структуру с использованием 1001 элементов вместо вставки одного элемента просто ужасно.

параллелизм. Если у вас есть несколько потоков, они могут захотеть получить текущую версию коллекции, а не версию, которая была передана 12 часов назад при запуске потока.

хранения. С неизменяемыми объектами в многопоточной среде вы можете получить десятки копий «одного» объекта в разные точки своего жизненного цикла. Не имеет значения для объекта Calendar или Date, но когда его коллекция из 10 000 виджетов убьет вас.

ответил James Anderson 19 ThuEurope/Moscow2013-12-19T05:15:47+04:00Europe/Moscow12bEurope/MoscowThu, 19 Dec 2013 05:15:47 +0400 2013, 05:15:47

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132