CopyOnWriteArrayList in Java with Examples - Java 147

CopyOnWriteArrayList in Java with Examples – Java 147

CopyOnWriteArrayList in Java with Examples

不在原有記憶體區塊中進行寫入操作,而是將記憶體複製一份,在新的記憶體中進行寫操作,寫完之後,利用加鎖保證同步,將指針指向新的記憶體,原來的記憶體就可以被回收掉,這是一種用於程式設計中的最佳化策略,是一種延時懶惰策略, CopyOnWriteArrayList in Java 介紹常見的 forEach 、 iterator 、 listIterator 、 spliterator 、 trySplit 等方法,本篇增加了範例,並透過單元測試來驗證產出結果。

檔案目錄

./
   +- src
       +- test
       |   +- org
       |       +- ruoxue
       |           +- java_147
       |               +- list
       |                   +- copyonwritearraylist
       |                       +- CopyOnWriteArrayListWithExamplesTest.java   

單元測試

CopyOnWriteArrayList Java 提供循環訪問、保留相同元素、轉成陣列等操作列表中的元素。

iteratorThrowException

建立一個 ArrayList ,增加三個元素,多執行緒進行讀取元素時,同時寫入元素,會拋出例外。

	@Test
	public void iteratorThrowException() {
		try {
			int poolSize = 3;
			ExecutorService executorService = Executors.newFixedThreadPool(poolSize);
			List<String> list = new ArrayList<>();
			list.add("Apple");
			list.add("Banana");
			list.add("Cherry");
			IntStream.range(0, poolSize).forEach(e -> {
				executorService.execute(() -> {
					Iterator<String> it = list.iterator();
					while (it.hasNext()) {
						System.out.println(String.format("[%d] %s", Thread.currentThread().getId(), it.next()));
					}
				});
			});

			IntStream.range(0, poolSize).forEach(e -> {
				executorService.execute(() -> {
					list.add("Grape");
				});
			});
			Thread.sleep(2_000L);
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}
[11] Apple
[13] Apple
[12] Apple
[11] Banana
[13] Banana
[12] Banana
[11] Cherry
[12] Cherry
[13] Cherry
Exception in thread "pool-1-thread-3" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
	at java.util.ArrayList$Itr.next(ArrayList.java:859)
	at org.ruoxue.java_147.list.copyonwritearraylist.CopyOnWriteArrayListWithExamplesTest.lambda$1(CopyOnWriteArrayListWithExamplesTest.java:33)

iterator

CopyOnWriteArrayList Java 建立一個 CopyOnWriteArrayList ,增加三個元素,多執行緒進行讀寫元素時,能夠安全地遍歷集合。

	@Test
	public void iterator() {
		try {
			int poolSize = 3;
			ExecutorService executorService = Executors.newFixedThreadPool(poolSize);
			List<String> list = new CopyOnWriteArrayList<>();
			list.add("Apple");
			list.add("Banana");
			list.add("Cherry");
			for (int i = 0; i < poolSize; i++) {
				executorService.execute(() -> {
					Iterator<String> it = list.iterator();
					while (it.hasNext()) {
						System.out.println(String.format("[%d] %s", Thread.currentThread().getId(), it.next()));
					}
				});
			}

			for (int i = 0; i < poolSize; i++) {
				executorService.execute(() -> {
					list.add("Grape");
				});
			}
			Thread.sleep(2_000L);
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}
[11] Apple
[12] Apple
[13] Apple
[12] Banana
[11] Banana
[13] Banana
[11] Cherry
[12] Cherry
[13] Cherry

listIteratorThrowException

CopyOnWriteArrayList Java 建立一個 CopyOnWriteArrayList ,使用 ListIterator 操作 add 、 set 、 remove 方法,會拋出例外。

	@Test
	public void listIteratorThrowException() {
		CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
		list.add("Apple");
		list.add("Banana");
		list.add("Cherry");
		ListIterator<String> it = list.listIterator();

		assertThatCode(() -> {
			it.add("Grape");
		}).isInstanceOf(UnsupportedOperationException.class);

		assertThatCode(() -> {
			it.set("Apple");
		}).isInstanceOf(UnsupportedOperationException.class);

		assertThatCode(() -> {
			while (it.hasNext()) {
				it.remove();
			}
		}).isInstanceOf(UnsupportedOperationException.class);
	}

loop

CopyOnWriteArrayList Java 建立一個 CopyOnWriteArrayList ,內有三個元素,迴圈取得元素。

	@Test
	public void loop() {
		List<String> list = new CopyOnWriteArrayList<>();
		list.add("Apple");
		list.add("Banana");
		list.add("Cherry");
		for (int i = 0; i < list.size(); i++) {
			System.out.println(list.get(i));
		}
	}
Apple
Banana
Cherry

forEach

Java CopyOnWriteArrayList 建立一個 CopyOnWriteArrayList ,內有三個元素,迴圈取得元素。

	@Test
	public void forEach() {
		List<String> list = new CopyOnWriteArrayList<>();
		list.add("Apple");
		list.add("Banana");
		list.add("Cherry");
		list.forEach(e -> System.out.println(e));
	}
Apple
Banana
Cherry

forEachRemaining

Java CopyOnWriteArrayList 建立一個 CopyOnWriteArrayList ,內有三個元素,迴圈取得剩餘元素。

	@Test
	public void forEachRemaining() {
		List<String> list = new CopyOnWriteArrayList<>();
		list.add("Apple");
		list.add("Banana");
		list.add("Cherry");
		Iterator<String> it = list.iterator();
		int i = 0;
		while (it.hasNext()) {
			System.out.println(it.next());
			if (i == 1) {
				break;
			}
			i++;
		}
		System.out.println("----------");
		it.forEachRemaining(e -> {
			System.out.println(e);
		});
	}
Apple
Banana
----------
Cherry



Apple
Banana
Cherry

listIterator

Java CopyOnWriteArrayList 建立一個 CopyOnWriteArrayList ,內有三個元素,迴圈取得元素。

	@Test
	public void listIterator() {
		List<String> list = new CopyOnWriteArrayList<>();
		list.add("Apple");
		list.add("Banana");
		list.add("Cherry");
		ListIterator<String> it = list.listIterator(list.size());
		while (it.hasPrevious()) {
			System.out.println(it.previous());
		}
	}
Cherry
Banana
Apple

spliterator

Java CopyOnWriteArrayList Example 建立一個 CopyOnWriteArrayList ,內有三個元素,使用 spliterator 取得元素,等同於 iterator.hasNext 和 iterator.next。

	@Test
	public void spliterator() {
		List<String> list = new CopyOnWriteArrayList<>();
		list.add("Apple");
		list.add("Banana");
		list.add("Cherry");
		Spliterator<String> sit = list.spliterator();
		sit.tryAdvance(e -> System.out.println(e));
		System.out.println("----------");
		sit.forEachRemaining(e -> System.out.println(e));

		System.out.println("----------");
		sit = list.spliterator();
		while (sit.tryAdvance(e -> System.out.println(e))) {
		}
	}
Apple
----------
Banana
Cherry
----------
Apple
Banana
Cherry

trySplit

Java CopyOnWriteArrayList Example 建立一個 CopyOnWriteArrayList ,內有三個元素,使用 spliterator 取得元素,trySplit 將目前的拆分器分為兩個新的拆分器並行處理。

	@Test
	public void trySplit() {
		List<String> list = new CopyOnWriteArrayList<>();
		list.add("Apple");
		list.add("Banana");
		list.add("Cherry");
		Spliterator<String> sit = list.spliterator();
		Spliterator<String> sit2 = sit.trySplit();
		System.out.println(sit.getExactSizeIfKnown());
		sit.forEachRemaining(e -> System.out.println(e));

		System.out.println("----------");
		System.out.println(sit2.getExactSizeIfKnown());
		sit2.forEachRemaining(e -> System.out.println(e));
	}
2
Banana
Cherry
----------
1
Apple

toArray

Java CopyOnWriteArrayList Example 建立一個 CopyOnWriteArrayList ,內有三個元素,轉換成 String 陣列。

	@Test
	public void toArray() {
		int expectedSize = 3;
		List<String> list = new CopyOnWriteArrayList<>();
		list.add("Apple");
		list.add("Banana");
		list.add("Cherry");

		String[] array = new String[list.size()];
		list.toArray(array);
		for (String e : array) {
			System.out.println(e);
		}
		assertEquals(expectedSize, array.length);
	}
Apple
Banana
Cherry

streamToArray

建立一個 CopyOnWriteArrayList ,內有三個元素,使用 Stream ,轉換成 String 陣列。

	@Test
	public void streamToArray() {
		int expectedSize = 3;
		List<String> list = new CopyOnWriteArrayList<>();
		list.add("Apple");
		list.add("Banana");
		list.add("Cherry");

		String[] array = list.stream().toArray(String[]::new);
		for (String e : array) {
			System.out.println(e);
		}
		assertEquals(expectedSize, array.length);
	}
Apple
Banana
Cherry	

CopyOnWriteArrayListWithExamplesTest.java

Java CopyOnWriteArrayList 新增單元測試,驗證 Java CopyOnWriteArrayList Example 是否符合預期。

package org.ruoxue.java_147.list.copyonwritearraylist;

import static org.assertj.core.api.Assertions.assertThatCode;
import static org.junit.Assert.assertEquals;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Spliterator;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.IntStream;

import org.junit.Test;

public class CopyOnWriteArrayListWithExamplesTest {

	@Test
	public void iteratorThrowException() {
		try {
			int poolSize = 3;
			ExecutorService executorService = Executors.newFixedThreadPool(poolSize);
			List<String> list = new ArrayList<>();
			list.add("Apple");
			list.add("Banana");
			list.add("Cherry");
			IntStream.range(0, poolSize).forEach(e -> {
				executorService.execute(() -> {
					Iterator<String> it = list.iterator();
					while (it.hasNext()) {
						System.out.println(String.format("[%d] %s", Thread.currentThread().getId(), it.next()));
					}
				});
			});

			IntStream.range(0, poolSize).forEach(e -> {
				executorService.execute(() -> {
					list.add("Grape");
				});
			});
			Thread.sleep(2_000L);
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}

	@Test
	public void iterator() {
		try {
			int poolSize = 3;
			ExecutorService executorService = Executors.newFixedThreadPool(poolSize);
			List<String> list = new CopyOnWriteArrayList<>();
			list.add("Apple");
			list.add("Banana");
			list.add("Cherry");
			for (int i = 0; i < poolSize; i++) {
				executorService.execute(() -> {
					Iterator<String> it = list.iterator();
					while (it.hasNext()) {
						System.out.println(String.format("[%d] %s", Thread.currentThread().getId(), it.next()));
					}
				});
			}

			for (int i = 0; i < poolSize; i++) {
				executorService.execute(() -> {
					list.add("Grape");
				});
			}
			Thread.sleep(2_000L);
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}

	@Test
	public void listIteratorThrowException() {
		CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
		list.add("Apple");
		list.add("Banana");
		list.add("Cherry");
		ListIterator<String> it = list.listIterator();

		assertThatCode(() -> {
			it.add("Grape");
		}).isInstanceOf(UnsupportedOperationException.class);

		assertThatCode(() -> {
			it.set("Apple");
		}).isInstanceOf(UnsupportedOperationException.class);

		assertThatCode(() -> {
			while (it.hasNext()) {
				it.remove();
			}
		}).isInstanceOf(UnsupportedOperationException.class);
	}

	@Test
	public void loop() {
		List<String> list = new CopyOnWriteArrayList<>();
		list.add("Apple");
		list.add("Banana");
		list.add("Cherry");
		for (int i = 0; i < list.size(); i++) {
			System.out.println(list.get(i));
		}
	}

	@Test
	public void forEach() {
		List<String> list = new CopyOnWriteArrayList<>();
		list.add("Apple");
		list.add("Banana");
		list.add("Cherry");
		list.forEach(e -> System.out.println(e));
	}

	@Test
	public void forEachRemaining() {
		List<String> list = new CopyOnWriteArrayList<>();
		list.add("Apple");
		list.add("Banana");
		list.add("Cherry");
		Iterator<String> it = list.iterator();
		int i = 0;
		while (it.hasNext()) {
			System.out.println(it.next());
			if (i == 1) {
				break;
			}
			i++;
		}
		System.out.println("----------");
		it.forEachRemaining(e -> {
			System.out.println(e);
		});
	}

	@Test
	public void listIterator() {
		List<String> list = new CopyOnWriteArrayList<>();
		list.add("Apple");
		list.add("Banana");
		list.add("Cherry");
		ListIterator<String> it = list.listIterator(list.size());
		while (it.hasPrevious()) {
			System.out.println(it.previous());
		}
	}

	@Test
	public void spliterator() {
		List<String> list = new CopyOnWriteArrayList<>();
		list.add("Apple");
		list.add("Banana");
		list.add("Cherry");
		Spliterator<String> sit = list.spliterator();
		sit.tryAdvance(e -> System.out.println(e));
		System.out.println("----------");
		sit.forEachRemaining(e -> System.out.println(e));

		System.out.println("----------");
		sit = list.spliterator();
		while (sit.tryAdvance(e -> System.out.println(e))) {
		}
	}

	@Test
	public void trySplit() {
		List<String> list = new CopyOnWriteArrayList<>();
		list.add("Apple");
		list.add("Banana");
		list.add("Cherry");
		Spliterator<String> sit = list.spliterator();
		Spliterator<String> sit2 = sit.trySplit();
		System.out.println(sit.getExactSizeIfKnown());
		sit.forEachRemaining(e -> System.out.println(e));

		System.out.println("----------");
		System.out.println(sit2.getExactSizeIfKnown());
		sit2.forEachRemaining(e -> System.out.println(e));
	}

	@Test
	public void toArray() {
		int expectedSize = 3;
		List<String> list = new CopyOnWriteArrayList<>();
		list.add("Apple");
		list.add("Banana");
		list.add("Cherry");

		String[] array = new String[list.size()];
		list.toArray(array);
		for (String e : array) {
			System.out.println(e);
		}
		assertEquals(expectedSize, array.length);
	}

	@Test
	public void streamToArray() {
		int expectedSize = 3;
		List<String> list = new CopyOnWriteArrayList<>();
		list.add("Apple");
		list.add("Banana");
		list.add("Cherry");

		String[] array = list.stream().toArray(String[]::new);
		for (String e : array) {
			System.out.println(e);
		}
		assertEquals(expectedSize, array.length);
	}
}

心得分享

Java CopyOnWriteArrayList Example 當任何修改方法,例如: add 或 remove 時,全部內容都會複製到新的內部副本中,即使發生並發修改,也可以安全的方式遍歷集合,提供了幾種 CopyOnWriteArrayList 常見方法的操作範例,在應用上相當廣泛,熟悉 Java CopyOnWriteArrayList 這些方法的操作,可以快速撰寫程式,降低錯誤率,再輔以單元測試驗證,建置高效穩定的服務或系統。

發佈留言