Рубрики
Без рубрики

Руководство по копирайтингу

Краткий обзор списка CopyOnWriteArrayList Java и его наиболее распространенных применений.

Автор оригинала: baeldung.

1. Обзор

В этой краткой статье мы рассмотрим CopyOnWriteArrayList | из пакета java.util.concurrent .

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

2. API CopyOnWriteArrayList

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

Благодаря этому простому факту мы можем безопасно перебирать список, даже если происходит одновременное изменение .

Когда мы вызываем метод iterator() в списке CopyOnWriteArrayList, мы получаем Итератор , подкрепленный неизменяемым снимком содержимого списка CopyOnWriteArrayList .

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

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

3. Повторение CopyOnWriteArrayList При Вставке

Допустим, мы создаем экземпляр CopyOnWriteArrayList , в котором хранятся целые числа:

CopyOnWriteArrayList numbers 
  = new CopyOnWriteArrayList<>(new Integer[]{1, 3, 5, 8});

Затем мы хотим выполнить итерацию по этому массиву, поэтому мы создаем Итератор экземпляр:

Iterator iterator = numbers.iterator();

После создания Итератора мы добавляем новый элемент в список чисел :

numbers.add(10);

Имейте в виду, что, когда мы создаем итератор для CopyOnWriteArrayList, мы получаем неизменяемый снимок данных в списке во время вызова iterator () .

Из-за этого, повторяя его, мы не увидим числа 10 в итерации:

List result = new LinkedList<>();
iterator.forEachRemaining(result::add);
 
assertThat(result).containsOnly(1, 3, 5, 8);

Последующая итерация с использованием вновь созданного Итератора также вернет добавленное число 10:

Iterator iterator2 = numbers.iterator();
List result2 = new LinkedList<>();
iterator2.forEachRemaining(result2::add);

assertThat(result2).containsOnly(1, 3, 5, 8, 10);

4. Удаление Во Время Итерации Не Допускается

CopyOnWriteArrayList был создан для обеспечения возможности безопасного перебора элементов даже при изменении базового списка.

Из – за механизма копирования операция remove() для возвращенного Итератора не разрешена- в результате Исключение UnsupportedOperationException:

@Test(expected = UnsupportedOperationException.class)
public void whenIterateOverItAndTryToRemoveElement_thenShouldThrowException() {
    
    CopyOnWriteArrayList numbers
      = new CopyOnWriteArrayList<>(new Integer[]{1, 3, 5, 8});

    Iterator iterator = numbers.iterator();
    while (iterator.hasNext()) {
        iterator.remove();
    }
}

5. Заключение

В этом кратком руководстве мы рассмотрели реализацию CopyOnWriteArrayList из пакета java.util.concurrent .

Мы увидели интересную семантику этого списка и то, как его можно повторять потокобезопасным способом, в то время как другие потоки могут продолжать вставлять или удалять элементы из него.

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