Как вернуть потокобезопасную / неизменяемую коллекцию в Java?
В проекте, который я пишу, мне нужно вернуть потокобезопасное и неизменное представление из функции. Однако я не уверен в этом. поскольку synchronizedList
а также unmodifiableList
просто вернуть представления списка, я не знаю, если
Collections.synchronizedList(Collections.unmodifiableList(this.data));
сделал бы трюк.
Может ли кто-нибудь сказать мне, если это правильно, и если это не так, есть ли ситуации, которые это может потерпеть неудачу?
Спасибо за любые вклады!
6 ответов
Я считаю, что это настоящий пробел в JDK. К счастью, команда Google, возглавляемая дизайнером Java Collections Джошуа Блохом, создала библиотеку, которая включает в себя действительно неизменные коллекции.
В частности, ImmutableList - это та реализация, которую вы ищете. Вот краткий набросок некоторых функций ImmutableCollections Guava.
Я думаю, что немодифицируемого достаточно. Вы не можете писать в него, что вызывает проблемы для многопоточного доступа. Это только для чтения, поэтому дополнительный шаг синхронизации кажется мне ненужным.
Лучше всего проверить исходный код, когда есть такие вопросы. Похоже, это возвращает UnmodifiableList
:
/**
* @serial include
*/
static class UnmodifiableList<E> extends UnmodifiableCollection<E>
implements List<E> {
static final long serialVersionUID = -283967356065247728L;
final List<? extends E> list;
UnmodifiableList(List<? extends E> list) {
super(list);
this.list = list;
}
public boolean equals(Object o) {return o == this || list.equals(o);}
public int hashCode() {return list.hashCode();}
public E get(int index) {return list.get(index);}
public E set(int index, E element) {
throw new UnsupportedOperationException();
}
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
public E remove(int index) {
throw new UnsupportedOperationException();
}
public int indexOf(Object o) {return list.indexOf(o);}
public int lastIndexOf(Object o) {return list.lastIndexOf(o);}
public boolean addAll(int index, Collection<? extends E> c) {
throw new UnsupportedOperationException();
}
public ListIterator<E> listIterator() {return listIterator(0);}
public ListIterator<E> listIterator(final int index) {
return new ListIterator<E>() {
ListIterator<? extends E> i = list.listIterator(index);
public boolean hasNext() {return i.hasNext();}
public E next() {return i.next();}
public boolean hasPrevious() {return i.hasPrevious();}
public E previous() {return i.previous();}
public int nextIndex() {return i.nextIndex();}
public int previousIndex() {return i.previousIndex();}
public void remove() {
throw new UnsupportedOperationException();
}
public void set(E e) {
throw new UnsupportedOperationException();
}
public void add(E e) {
throw new UnsupportedOperationException();
}
};
}
Collections.unmodifiableList(this.data)
Сделаем, как вернем взгляд. Любые попытки изменения в этом представлении приведут к UnsupportedOperationException
быть брошенным Ниже приведены выдержки из Collections#unmodifiableList
документация.
Возвращает неизменяемое представление указанного списка. Этот метод позволяет модулям предоставлять пользователям доступ "только для чтения" к внутренним спискам. Операции запроса в возвращаемом списке "считываются" в указанный список и пытаются изменить возвращенный список, будь то прямой или через его итератор, приводят к исключению UnsupportedOperationException.
......
copyOf
Да, теперь встроен в Java 10 и новее.
Каждый из них возвращает отдельную коллекцию объектов, найденных в оригинале. Возвращенная коллекция не является видом на оригинал, как в случае сCollections.unmodifiable…
методы служебного класса.
Java 9+ ImmutableCollections является потокобезопасным. Например, List.of, Map.of ... Согласно документу oracle,
Одним из преимуществ неизменяемой коллекции является то, что она автоматически потокобезопасна. После создания коллекции вы можете передать ее нескольким потокам, и все они будут видеть согласованное представление.
Подробнее на: oracle docs
Эти представления не вернут вам действительно поточно-ориентированные коллекции. Всегда существует вероятность того, что кто-либо изменит базовую коллекцию или элементы коллекции.
Чтобы решить эту проблему, вам нужно использовать неизменяемые коллекции и неизменные элементы. Затем в результате возникает потокобезопасность.
Clojure содержит такие неизменные (или постоянные) коллекции.
Проще говоря, добавление или удаление новых элементов возвращает новую коллекцию, которая, как правило , повторно использует большие части старой коллекции посредством умного использования структур данных типа Trie.
Сами по себе они плохо подходят для использования на прямой Java.
Pure4j - это попытка перенести их (и стиль, основанный на неизменности / ценности, поддерживаемый Clojure) на язык Java. Это может быть то, что вы после.
Отказ от ответственности: я разработчик Pure4J