Table of Contents
ToggleCopyOnWriteArraySet in Java with Examples
這是一種用於程式設計中的最佳化策略,延時懶惰策略,Iterator 支援 hasNext、 next 等不可變操作,但不支援可變 remove 等操作,使用 Iterator 進行遍歷的速度很快,不會與其他執行緒發生衝突,在建構 Iterator 時,依賴不變的陣列快照, CopyOnWriteArraySet in Java 介紹常見的 forEach 、 iterator 、 spliterator 、 trySplit 等方法,本篇增加了範例,並透過單元測試來驗證產出結果。
檔案目錄
./
+- src
+- test
| +- org
| +- ruoxue
| +- java_147
| +- set
| +- copyonwritearrayset
| +- CopyOnWriteArraySetWithExamplesTest.java
單元測試
CopyOnWriteArraySet Java 提供循環訪問、保留相同元素、轉成陣列等操作 Set 中的元素。
iteratorThrowException
建立一個 HashSet ,增加三個元素,多執行緒進行讀取元素時,同時寫入元素,會拋出例外。
@Test
public void iteratorThrowException() {
try {
int poolSize = 3;
ExecutorService executorService = Executors.newFixedThreadPool(poolSize);
Set<String> set = new HashSet<>();
set.add("Longan");
set.add("Tomato");
set.add("Pear");
IntStream.range(0, poolSize).forEach(e -> {
executorService.execute(() -> {
Iterator<String> it = set.iterator();
while (it.hasNext()) {
System.out.println(String.format("[%d] %s", Thread.currentThread().getId(), it.next()));
}
});
});
IntStream.range(0, poolSize).forEach(e -> {
executorService.execute(() -> {
set.add("Grape");
});
});
Thread.sleep(2_000L);
} catch (Exception ex) {
ex.printStackTrace();
}
}
[11] Pear
[13] Pear
[11] Longan
[12] Pear
[11] Tomato
[12] Longan
[13] Longan
Exception in thread "pool-1-thread-2" Exception in thread "pool-1-thread-3" java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextNode(HashMap.java:1445)
at java.util.HashMap$KeyIterator.next(HashMap.java:1469)
at org.ruoxue.java_147.set.copyonwritearrayset.CopyOnWriteArraySetWithExamplesTest.lambda$1(CopyOnWriteArraySetWithExamplesTest.java:32)
iterator
CopyOnWriteArraySet Java 建立一個 CopyOnWriteArraySet ,增加三個元素,多執行緒進行讀寫元素時,能夠安全地遍歷集合。
@Test
public void iterator() {
try {
int poolSize = 3;
ExecutorService executorService = Executors.newFixedThreadPool(poolSize);
Set<String> set = new CopyOnWriteArraySet<>();
set.add("Longan");
set.add("Tomato");
set.add("Pear");
for (int i = 0; i < poolSize; i++) {
executorService.execute(() -> {
Iterator<String> it = set.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(() -> {
set.add("Grape");
});
}
Thread.sleep(2_000L);
} catch (Exception ex) {
ex.printStackTrace();
}
}
[12] Longan
[11] Longan
[12] Tomato
[13] Longan
[11] Tomato
[13] Tomato
[11] Pear
[13] Pear
[12] Pear
iteratorRemoveThrowException
CopyOnWriteArraySet Java 建立一個 CopyOnWriteArraySet ,使用 Iterator 操作 remove 方法,會拋出例外。
@Test
public void iteratorRemoveThrowException() {
CopyOnWriteArraySet<String> set = new CopyOnWriteArraySet<>();
set.add("Longan");
set.add("Tomato");
set.add("Pear");
Iterator<String> it = set.iterator();
assertThatCode(() -> {
while (it.hasNext()) {
it.remove();
}
}).isInstanceOf(UnsupportedOperationException.class);
}
forEach
Java CopyOnWriteArraySet 建立一個 CopyOnWriteArraySet ,內有三個元素,迴圈取得元素。
@Test
public void forEach() {
Set<String> set = new CopyOnWriteArraySet<>();
set.add("Longan");
set.add("Tomato");
set.add("Pear");
set.forEach(e -> System.out.println(e));
}
Longan
Tomato
Pear
forEachRemaining
Java CopyOnWriteArraySet 建立一個 CopyOnWriteArraySet ,內有三個元素,迴圈取得剩餘元素。
@Test
public void forEachRemaining() {
Set<String> set = new CopyOnWriteArraySet<>();
set.add("Longan");
set.add("Tomato");
set.add("Pear");
Iterator<String> it = set.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);
});
}
Longan
Tomato
----------
Pear
spliterator
Java CopyOnWriteArraySet Example 建立一個 CopyOnWriteArraySet ,內有三個元素,使用 spliterator 取得元素,等同於 iterator.hasNext 和 iterator.next。
@Test
public void spliterator() {
Set<String> set = new CopyOnWriteArraySet<>();
set.add("Longan");
set.add("Tomato");
set.add("Pear");
Spliterator<String> sit = set.spliterator();
sit.tryAdvance(e -> System.out.println(e));
System.out.println("----------");
sit.forEachRemaining(e -> System.out.println(e));
System.out.println("----------");
sit = set.spliterator();
while (sit.tryAdvance(e -> System.out.println(e))) {
}
}
Longan
----------
Tomato
Pear
----------
Longan
Tomato
Pear
trySplit
Java CopyOnWriteArraySet Example 建立一個 CopyOnWriteArraySet ,內有三個元素,使用 spliterator 取得元素,trySplit 將目前的拆分器分為兩個新的拆分器並行處理。
@Test
public void trySplit() {
Set<String> set = new CopyOnWriteArraySet<>();
set.add("Longan");
set.add("Tomato");
set.add("Pear");
Spliterator<String> sit = set.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
Tomato
Pear
----------
1
Longan
toArray
Java CopyOnWriteArraySet Example 建立一個 CopyOnWriteArraySet ,內有三個元素,轉換成 String 陣列。
@Test
public void toArray() {
int expectedSize = 3;
Set<String> set = new CopyOnWriteArraySet<>();
set.add("Longan");
set.add("Tomato");
set.add("Pear");
String[] array = new String[set.size()];
set.toArray(array);
for (String e : array) {
System.out.println(e);
}
assertEquals(expectedSize, array.length);
}
Longan
Tomato
Pear
streamToArray
建立一個 CopyOnWriteArraySet ,內有三個元素,使用 Stream ,轉換成 String 陣列。
@Test
public void streamToArray() {
int expectedSize = 3;
Set<String> set = new CopyOnWriteArraySet<>();
set.add("Longan");
set.add("Tomato");
set.add("Pear");
String[] array = set.stream().toArray(String[]::new);
for (String e : array) {
System.out.println(e);
}
assertEquals(expectedSize, array.length);
}
Longan
Tomato
Pear
CopyOnWriteArraySetWithExamplesTest.java
Java CopyOnWriteArraySet 新增單元測試,驗證 Java CopyOnWriteArraySet Example 是否符合預期。
package org.ruoxue.java_147.set.copyonwritearrayset;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.junit.Assert.assertEquals;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.Spliterator;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.IntStream;
import org.junit.Test;
public class CopyOnWriteArraySetWithExamplesTest {
@Test
public void iteratorThrowException() {
try {
int poolSize = 3;
ExecutorService executorService = Executors.newFixedThreadPool(poolSize);
Set<String> set = new HashSet<>();
set.add("Longan");
set.add("Tomato");
set.add("Pear");
IntStream.range(0, poolSize).forEach(e -> {
executorService.execute(() -> {
Iterator<String> it = set.iterator();
while (it.hasNext()) {
System.out.println(String.format("[%d] %s", Thread.currentThread().getId(), it.next()));
}
});
});
IntStream.range(0, poolSize).forEach(e -> {
executorService.execute(() -> {
set.add("Grape");
});
});
Thread.sleep(2_000L);
} catch (Exception ex) {
ex.printStackTrace();
}
}
@Test
public void iterator() {
try {
int poolSize = 3;
ExecutorService executorService = Executors.newFixedThreadPool(poolSize);
Set<String> set = new CopyOnWriteArraySet<>();
set.add("Longan");
set.add("Tomato");
set.add("Pear");
for (int i = 0; i < poolSize; i++) {
executorService.execute(() -> {
Iterator<String> it = set.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(() -> {
set.add("Grape");
});
}
Thread.sleep(2_000L);
} catch (Exception ex) {
ex.printStackTrace();
}
}
@Test
public void iteratorRemoveThrowException() {
CopyOnWriteArraySet<String> set = new CopyOnWriteArraySet<>();
set.add("Longan");
set.add("Tomato");
set.add("Pear");
Iterator<String> it = set.iterator();
assertThatCode(() -> {
while (it.hasNext()) {
it.remove();
}
}).isInstanceOf(UnsupportedOperationException.class);
}
@Test
public void forEach() {
Set<String> set = new CopyOnWriteArraySet<>();
set.add("Longan");
set.add("Tomato");
set.add("Pear");
set.forEach(e -> System.out.println(e));
}
@Test
public void forEachRemaining() {
Set<String> set = new CopyOnWriteArraySet<>();
set.add("Longan");
set.add("Tomato");
set.add("Pear");
Iterator<String> it = set.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 spliterator() {
Set<String> set = new CopyOnWriteArraySet<>();
set.add("Longan");
set.add("Tomato");
set.add("Pear");
Spliterator<String> sit = set.spliterator();
sit.tryAdvance(e -> System.out.println(e));
System.out.println("----------");
sit.forEachRemaining(e -> System.out.println(e));
System.out.println("----------");
sit = set.spliterator();
while (sit.tryAdvance(e -> System.out.println(e))) {
}
}
@Test
public void trySplit() {
Set<String> set = new CopyOnWriteArraySet<>();
set.add("Longan");
set.add("Tomato");
set.add("Pear");
Spliterator<String> sit = set.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;
Set<String> set = new CopyOnWriteArraySet<>();
set.add("Longan");
set.add("Tomato");
set.add("Pear");
String[] array = new String[set.size()];
set.toArray(array);
for (String e : array) {
System.out.println(e);
}
assertEquals(expectedSize, array.length);
}
@Test
public void streamToArray() {
int expectedSize = 3;
Set<String> set = new CopyOnWriteArraySet<>();
set.add("Longan");
set.add("Tomato");
set.add("Pear");
String[] array = set.stream().toArray(String[]::new);
for (String e : array) {
System.out.println(e);
}
assertEquals(expectedSize, array.length);
}
}
心得分享
Java CopyOnWriteArraySet Example 當任何修改方法,例如: add 或 remove 時,全部內容都會複製到新的內部副本中,即使發生並發修改,也可以安全的方式遍歷集合,提供了幾種 CopyOnWriteArraySet 常見方法的操作範例,在應用上相當廣泛,熟悉 Java CopyOnWriteArraySet 這些方法的操作,可以快速撰寫程式,降低錯誤率,再輔以單元測試驗證,建置高效穩定的服務或系統。