За последний год я прочитал несколько блогов, статей и твитов о предстоящей функции в Java 9. Некоторые из статей были озаглавлены Неизменяемые коллекции в Java 9. В интерфейсах коллекций Java (List, Set, Map) появятся новые фабричные методы, которые будут создавать компактные, неизменяемые экземпляры коллекций. Эта функция описана в JEP 269 - http://openjdk.java.net/jeps/269. Эта функция будет долгожданным дополнением для многих разработчиков Java, особенно в таких местах, как модульные тесты, где создание небольших коллекций может быть частым и потенциально обременительным занятием.

Но… мы не получаем неизменяемые по контракту коллекции в Java 9, и JEP 269 не претендует на их предоставление. Если бы мы получали неизменяемые по контракту коллекции в Java 9, я бы ожидал увидеть новые интерфейсы с именами ImmutableCollection, ImmutableSet, ImmutableList и ImmutableMap. Эти интерфейсы могут иметь фабричные методы «of» для создания неизменяемых экземпляров коллекции, безопасных во время компиляции. ImmutableCollection интерфейсы не должны иметь таких методов, как _6 _ / _ 7_, _8 _ / _ 9_, потому что эти методы небезопасны и могут вызывать только UnsupportedOperationException.

Если вам нужны неизменяемые по контракту коллекции для Java, сегодня доступно несколько библиотек с открытым исходным кодом, которые их предоставляют. Eclipse Collections предоставляет как изменяемые, так и неизменяемые коллекции, а также имеет фабрики коллекций для них обоих. В части 1 этого блога я сравню изменяемые фабричные методы, доступные в коллекциях Eclipse, с новыми фабричными методами, которые будут добавлены в Java 9. Во второй части я исследую фабрики, доступные для неизменяемых коллекций.

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

MutableList<String> list = 
    FastList.newListWith("1", "2", "3");
MutableSet<String> set = 
    UnifiedSet.newSetWith("1", "2", "3");
MutableMap<Integer, String> map = 
    UnifiedMap.newWithKeysValues(1, "1", 2, "2", 3, "3");

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

Когда в библиотеке были введены неизменяемые коллекции, нам нужно было принять решение, как их создавать. Нам нужен последовательный подход к созданию как изменяемых, так и неизменяемых коллекций, которые следовали бы согласованному соглашению, которое было бы легко освоить разработчикам. Нашим решением было создание выделенных фабричных классов коллекций. Мы решили, что соглашение, которое мы будем использовать с нашими фабричными классами, будет соответствовать имеющимся у нас типам контейнеров с соответствующими типами, оканчивающимися на s. Итак, List (s), Set (s), Map (s) и т. Д. Мы стремимся к хорошей симметрии здесь, чтобы мы могли оправдать ожидания разработчиков. Вот эквивалентный код для создания изменяемых экземпляров коллекции с использованием фабричных классов.

MutableList<String> list = 
    Lists.mutable.with("1", "2", "3");
MutableSet<String> set = 
    Sets.mutable.with("1", "2", "3");
MutableMap<Integer, String> map = 
    Maps.mutable.with(1, "1", 2, "2");

Если вы хотите создать пустые изменяемые коллекции, просто используйте метод empty().

MutableList<String> list = 
    Lists.mutable.empty();
MutableSet<String> set = 
    Sets.mutable.empty();
MutableMap<Integer, String> map = 
    Maps.mutable.empty();

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

MutableList<String> list = 
    mList("1", "2", "3");
MutableSet<String> set = 
    mSet("1", "2", "3");
MutableMap<Integer, String> map = 
    mMap(1, "1", 2, "2", 3, "3");

Методы «m» являются сокращением от «изменяемый», а также существуют методы «i», сокращенно от слова «неизменяемый». .

Все изменяемые интерфейсы в коллекциях Eclipse расширяют соответствующие изменяемые интерфейсы в JDK. Итак, MutableList - это java.util.List, MutableSet - это java.util.Set и т. Д.

Изменяемые фабрики будут возвращать наиболее конкретный тип (например, MutableList), но вы можете использовать более абстрактный тип (например, java.util.List).

List<String> list = 
    Lists.mutable.with("1", "2", "3");
Set<String> set = 
    Sets.mutable.with("1", "2", "3");
Map<Integer, String> map = 
    Maps.mutable.with(1, "1", 2, "2", 3, "3");

Заводские методы, добавляемые в Java 9, будут выглядеть следующим образом.

List<String> list = 
    List.of("1", "2", "3");
Set<String> set = 
    Set.of("1", "2", "3");
Map<Integer, String> map = 
    Map.of(1, "1", 2, "2", 3, "3");

Разница в том, что указанные выше методы возвращают неизменяемые экземпляры с изменяемыми интерфейсами.

Если вы хотите использовать интерфейсы java.util.Collection и хотите, чтобы возвращаемые экземпляры были неизменяемыми, как приведенные выше фабричные методы Java 9, вы можете использовать метод asUnmodifiable(), доступный для всех изменяемых коллекций в Eclipse Collections.

List<String> list = 
    Lists.mutable.with("1", "2", "3").asUnmodifiable();
Set<String> set = 
    Sets.mutable.with("1", "2", "3").asUnmodifiable();
Map<Integer, String> map = 
    Maps.mutable.with(1, "1", 2, "2", 3, "3").asUnmodifiable();

Если вы хотите, чтобы они синхронизировались, вы можете использовать asSynchronized().

List<String> list = 
    Lists.mutable.with("1", "2", "3").asSynchronized();
Set<String> set = 
    Sets.mutable.with("1", "2", "3").asSynchronized();
Map<Integer, String> map = 
    Maps.mutable.with(1, "1", 2, "2", 3, "3").asSynchronized();

Ниже показаны все фабрики для коллекций изменяемых объектов, которые доступны в Коллекциях Eclipse.

MutableList<T> list = Lists.mutable.empty();
MutableSet<T> set = Sets.mutable.empty();
MutableSortedSet<T> sortedSet = SortedSets.mutable.empty();
MutableMap<K, V> map = Maps.mutable.empty();
MutableSortedMap<K, V> sortedMap = SortedMaps.mutable.empty();
MutableStack<T> stack = Stacks.mutable.empty();
MutableBag<T> bag = Bags.mutable.empty();
MutableSortedBag<T> sortedBag = SortedBags.mutable.empty();
MutableBiMap<K, V> biMap = BiMaps.mutable.empty();
MutableListMultimap<K, V> multimap = Multimaps.mutable.list.empty();
MutableSetMultimap<K, V> multimap = Multimaps.mutable.set.empty();
MutableBagMultimap<K, V> multimap = Multimaps.mutable.bag.empty();

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

MutableIntList list = 
    IntLists.mutable.empty();
MutableIntSet set = 
    IntSets.mutable.empty();
MutableIntBag bag = 
    IntBags.mutable.empty();
MutableIntStack stack = 
    IntStacks.mutable.empty();
// supports all combinations for all 8 primitives
MutableIntIntMap map = 
    IntIntMaps.mutable.empty();  
MutableIntObjectMap<V> map = 
    IntObjectMaps.mutable.empty();
MutableObjectIntMap<K> map = 
    ObjectIntMaps.mutable.empty();

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

MutableIntList list = 
    IntLists.mutable.with(1, 2, 3).asUnmodifiable();
MutableIntList list = 
    IntLists.mutable.with(1, 2, 3).asSynchronized();

Фабрики коллекций в Eclipse Collections работают со всеми версиями Java вплоть до версии 5. Если вы хотите использовать фабрики коллекций с версиями Java до Java 8, вам нужно будет использовать Eclipse Collections 7.x. Eclipse Collections 8.x совместим только с Java 8+. Обе коллекции Eclipse 7.x и 8.x работают с Java 8.

С из или из с

Все примеры, которые я показал до сих пор, в которых используется «с», также могут быть записаны с использованием «из». В битве имен между «с» и «из» было двое победителей. Таким образом, вы можете написать следующее, если предпочитаете вместо. FWIW, подход «с» больше соответствует стилю Smalltalk.

List<String> list = 
    Lists.mutable.of("1", "2", "3");
Set<String> set = 
    Sets.mutable.of("1", "2", "3");
Map<Integer, String> map = 
    Maps.mutable.of(1, "1", 2, "2", 3, "3");

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

Коллекции Eclipse открыты для взносов. Если вам нравится библиотека, вы можете сообщить нам об этом, отметив ее на GitHub.