Java : Collectors - API使用例
Collectors (Java SE 18 & JDK 18) の使用例まとめです。
だいたいのメソッドを網羅済みです。
API仕様のおともにどうぞ。
概要
Collectors クラスは、さまざまな処理をする Collector インタフェースを作成します。
Collectorインタフェースは、ストリームの終端操作である Stream.collect のパラメータに指定します。
ストリームを Map に変換する例です。
record Item(String name, int value) {
}
final var stream = Stream.of(
new Item("aaa", 10),
new Item("bbb", 20),
new Item("ccc", 30)
);
final var ret = stream.collect(
Collectors.toUnmodifiableMap(
Item::name, Item::value));
System.out.println(ret); // {aaa=10, ccc=30, bbb=20}
他にも、文字列として連結することもできます。
final var stream = Stream.of("aaa", "bbb", "ccc");
final var ret = stream.collect(Collectors.joining("-"));
System.out.println(ret); // aaa-bbb-ccc
関連記事:ストリームの基本 (Stream)
メソッド
static <T> Collector<T,?,Double> averagingDouble (ToDoubleFunction<? super T> mapper)
final var stream = Stream.of(1.0, 2.0, 3.0, 4.0);
final var ret = stream.collect(Collectors.averagingDouble(value -> value));
System.out.println(ret); // 2.5
static <T> Collector<T,?,Double> averagingInt (ToIntFunction<? super T> mapper)
final var stream = Stream.of(1, 2, 3, 4);
final var ret = stream.collect(Collectors.averagingInt(value -> value));
System.out.println(ret); // 2.5
static <T> Collector<T,?,Double> averagingLong (ToLongFunction<? super T> mapper)
final var stream = Stream.of(10000000000L, 20000000000L, 30000000000L, 40000000000L);
final var ret = stream.collect(Collectors.averagingLong(value -> value));
System.out.println(ret); // 2.5E10
static <T, A, R, RR> Collector<T,A,RR> collectingAndThen (Collector<T,A,R> downstream, Function<R,RR> finisher)
final var stream = Stream.of("aaa", "bbb", "ccc");
final var ret = stream.collect(Collectors.collectingAndThen(
Collectors.toList(),
Collections::unmodifiableList));
System.out.println(ret); // [aaa, bbb, ccc]
ret.add("xxx"); // java.lang.UnsupportedOperationException
static <T> Collector<T,?,Long> counting ()
final var stream = Stream.of("aaa", "bbb", "ccc");
final var ret = stream.collect(Collectors.counting());
System.out.println(ret); // 3
static <T, A, R> Collector<T,?,R> filtering (Predicate<? super T> predicate, Collector<? super T,A,R> downstream)
final var stream = Stream.of(1, 20, 3, 40);
final var ret = stream.collect(Collectors.filtering(
value -> value >= 10,
Collectors.toUnmodifiableList()));
System.out.println(ret); // [20, 40]
final var stream = Stream.of(1, 20, 3, 40);
final var ret = stream.collect(
Collectors.groupingBy(
(value) -> value % 2 == 0 ? "even" : "odd",
Collectors.filtering(
value -> value >= 10,
Collectors.toUnmodifiableList())));
System.out.println(ret); // {even=[20, 40], odd=[]}
static <T, U, A, R> Collector<T,?,R> flatMapping (Function<? super T,? extends Stream<? extends U>> mapper, Collector<? super U,A,R> downstream)
record Team(String name, List<String> members) {
}
final var stream = Stream.of(
new Team("aaa", List.of("a-1", "a-2")),
new Team("bbb", List.of("b-1", "b-2", "b-3"))
);
final var ret = stream.collect(
Collectors.flatMapping(
team -> team.members().stream(),
Collectors.toUnmodifiableList()));
System.out.println(ret); // [a-1, a-2, b-1, b-2, b-3]
record Team(String name, List<String> members) {
}
final var stream = Stream.of(
new Team("aaa", List.of("a-1", "a-2")),
new Team("aaa", List.of("a-3", "a-4")),
new Team("bbb", List.of("b-1", "b-2", "b-3"))
);
final var ret = stream.collect(
Collectors.groupingBy(
Team::name,
Collectors.flatMapping(
team -> team.members().stream(),
Collectors.toUnmodifiableList())));
System.out.println(ret); // {aaa=[a-1, a-2, a-3, a-4], bbb=[b-1, b-2, b-3]}
static <T, K> Collector<T,?,Map<K,List<T>>> groupingBy (Function<? super T,? extends K> classifier)
final var stream = Stream.of(1, 2, 3, 4, 5);
// 偶数・奇数でグルーピングします。
final var ret = stream.collect(
Collectors.groupingBy((value) -> value % 2 == 0 ? "even" : "odd"));
System.out.println(ret); // {even=[2, 4], odd=[1, 3, 5]}
static <T, K, D, A, M extends Map<K, D>> Collector<T,?,M> groupingBy (Function<? super T,? extends K> classifier, Supplier<M> mapFactory, Collector<? super T,A,D> downstream)
record Book(String category, String title, int price) {
}
final var stream = Stream.of(
new Book("Travel", "aaa", 10),
new Book("History", "bbb", 20),
new Book("Cooking", "ccc", 50),
new Book("Cooking", "ddd", 40),
new Book("History", "eee", 30),
new Book("History", "fff", 60)
);
final var ret = stream.collect(Collectors.groupingBy(
Book::category,
TreeMap::new,
Collectors.mapping(
Book::title,
Collectors.toUnmodifiableList())));
// {Cooking=[ccc, ddd], History=[bbb, eee, fff], Travel=[aaa]}
System.out.println(ret);
static <T, K, A, D> Collector<T,?,Map<K,D>> groupingBy (Function<? super T,? extends K> classifier, Collector<? super T,A,D> downstream)
record Book(String category, String title, int price) {
}
final var stream = Stream.of(
new Book("Travel", "aaa", 10),
new Book("History", "bbb", 20),
new Book("Cooking", "ccc", 50),
new Book("Cooking", "ddd", 40),
new Book("History", "eee", 30),
new Book("History", "fff", 60)
);
final var ret = stream.collect(Collectors.groupingBy(
Book::category,
Collectors.mapping(
Book::title,
Collectors.toUnmodifiableList())));
// {Travel=[aaa], Cooking=[ccc, ddd], History=[bbb, eee, fff]}
System.out.println(ret);
static <T, K> Collector<T,?,ConcurrentMap<K,List<T>>> groupingByConcurrent (Function<? super T,? extends K> classifier)
final var stream = Stream.of(1, 2, 3, 4, 5).parallel();
System.out.println(stream.isParallel()); // true
// 偶数・奇数でグルーピングします。
final var ret = stream.collect(
Collectors.groupingByConcurrent(
(value) -> value % 2 == 0 ? "even" : "odd"));
System.out.println(ret); // {even=[4, 2], odd=[3, 5, 1]}
static <T, K, A, D, M extends ConcurrentMap<K, D>> Collector<T,?,M> groupingByConcurrent (Function<? super T,? extends K> classifier, Supplier<M> mapFactory, Collector<? super T,A,D> downstream)
record Book(String category, String title, int price) {
}
final var stream = Stream.of(
new Book("Travel", "aaa", 10),
new Book("History", "bbb", 20),
new Book("Cooking", "ccc", 50),
new Book("Cooking", "ddd", 40),
new Book("History", "eee", 30),
new Book("History", "fff", 60)
).parallel();
System.out.println(stream.isParallel()); // true
final var ret = stream.collect(Collectors.groupingByConcurrent(
Book::category,
ConcurrentSkipListMap::new,
Collectors.mapping(
Book::title,
Collectors.toUnmodifiableList())));
// {Cooking=[ddd, ccc], History=[fff, eee, bbb], Travel=[aaa]}
System.out.println(ret);
static <T, K, A, D> Collector<T,?,ConcurrentMap<K,D>> groupingByConcurrent (Function<? super T,? extends K> classifier, Collector<? super T,A,D> downstream)
record Book(String category, String title, int price) {
}
final var stream = Stream.of(
new Book("Travel", "aaa", 10),
new Book("History", "bbb", 20),
new Book("Cooking", "ccc", 50),
new Book("Cooking", "ddd", 40),
new Book("History", "eee", 30),
new Book("History", "fff", 60)
).parallel();
System.out.println(stream.isParallel()); // true
final var ret = stream.collect(Collectors.groupingByConcurrent(
Book::category,
Collectors.mapping(
Book::title,
Collectors.toUnmodifiableList())));
// {Travel=[aaa], Cooking=[ddd, ccc], History=[eee, fff, bbb]}
System.out.println(ret);
static Collector<CharSequence,?,String> joining ()
final var stream = Stream.of("aaa", "XXX", "bbb", "YYY");
final var ret = stream.collect(Collectors.joining());
System.out.println(ret); // aaaXXXbbbYYY
static Collector<CharSequence,?,String> joining (CharSequence delimiter)
final var stream = Stream.of("aaa", "XXX", "bbb", "YYY");
final var ret = stream.collect(Collectors.joining("-"));
System.out.println(ret); // aaa-XXX-bbb-YYY
static Collector<CharSequence,?,String> joining (CharSequence delimiter, CharSequence prefix, CharSequence suffix)
final var stream = Stream.of("aaa", "XXX", "bbb", "YYY");
final var ret = stream.collect(Collectors.joining("-", "[", "]"));
System.out.println(ret); // [aaa-XXX-bbb-YYY]
static <T, U, A, R> Collector<T,?,R> mapping (Function<? super T,? extends U> mapper, Collector<? super U,A,R> downstream)
record Book(String category, String title, int price) {
}
final var stream = Stream.of(
new Book("Travel", "aaa", 10),
new Book("History", "bbb", 20),
new Book("Cooking", "ccc", 50),
new Book("Cooking", "ddd", 40),
new Book("History", "eee", 30)
);
final var ret = stream.collect(
Collectors.mapping(
Book::title,
Collectors.toUnmodifiableList()));
System.out.println(ret); // [aaa, bbb, ccc, ddd, eee]
record Book(String category, String title, int price) {
}
final var stream = Stream.of(
new Book("Travel", "aaa", 10),
new Book("History", "bbb", 20),
new Book("Cooking", "ccc", 50),
new Book("Cooking", "ddd", 40),
new Book("History", "eee", 30)
);
final var ret = stream.collect(Collectors.groupingBy(
Book::category,
Collectors.mapping(
Book::title,
Collectors.toUnmodifiableList())));
// {Travel=[aaa], Cooking=[ccc, ddd], History=[bbb, eee]}
System.out.println(ret);
static <T> Collector<T,?,Optional<T>> maxBy (Comparator<? super T> comparator)
final var stream = Stream.of(1, 100, -200, 50, -30);
final var ret = stream.collect(Collectors.maxBy(Comparator.naturalOrder()));
System.out.println(ret); // Optional[100]
final var stream = Stream.of("Tree", "book", "stone", "Cake");
final var ret = stream.collect(Collectors.maxBy(String.CASE_INSENSITIVE_ORDER));
System.out.println(ret); // Optional[Tree]
final var stream = Stream.<String>empty();
final var ret = stream.collect(Collectors.maxBy(Comparator.naturalOrder()));
System.out.println(ret); // Optional.empty
static <T> Collector<T,?,Optional<T>> minBy (Comparator<? super T> comparator)
final var stream = Stream.of(1, 100, -200, 50, -30);
final var ret = stream.collect(Collectors.minBy(Comparator.naturalOrder()));
System.out.println(ret); // Optional[-200]
final var stream = Stream.of("Tree", "book", "stone", "Cake");
final var ret = stream.collect(Collectors.minBy(String.CASE_INSENSITIVE_ORDER));
System.out.println(ret); // Optional[book]
final var stream = Stream.<String>of();
final var ret = stream.collect(Collectors.minBy(Comparator.naturalOrder()));
System.out.println(ret); // Optional.empty
static <T> Collector<T,?,Map<Boolean,List<T>>> partitioningBy (Predicate<? super T> predicate)
final var stream = Stream.of(1, 2, 3, 4, 5);
// true = 偶数, false = 奇数
final var ret = stream.collect(
Collectors.partitioningBy((value) -> value % 2 == 0));
System.out.println(ret); // {false=[1, 3, 5], true=[2, 4]}
static <T, D, A> Collector<T,?,Map<Boolean,D>> partitioningBy (Predicate<? super T> predicate, Collector<? super T,A,D> downstream)
record Item(Integer value, String name) {
}
final var stream = Stream.of(
new Item(1, "aaa"),
new Item(2, "bbb"),
new Item(3, "ccc"),
new Item(4, "ddd"),
new Item(5, "eee")
);
// true = 偶数, false = 奇数
final var ret = stream.collect(
Collectors.partitioningBy(
(item) -> item.value() % 2 == 0,
Collectors.mapping(
Item::name,
Collectors.toUnmodifiableList()
)));
System.out.println(ret); // {false=[aaa, ccc, eee], true=[bbb, ddd]}
static <T> Collector<T,?,Optional<T>> reducing (BinaryOperator<T> op)
record Book(String category, String title, int price) {
}
final var stream = Stream.of(
new Book("Travel", "aaa", 10),
new Book("History", "bbb", 20),
new Book("Cooking", "ccc", 50),
new Book("Cooking", "ddd", 40),
new Book("History", "eee", 30)
);
final var comparator = Comparator.comparing(Book::price);
final var map = stream.collect(
Collectors.groupingBy(
Book::category,
Collectors.reducing(
BinaryOperator.maxBy(comparator)
)));
System.out.println("-- entry --");
for (final var entry : map.entrySet()) {
System.out.println(entry);
}
// 結果
// ↓
//-- entry --
//Travel=Optional[Book[category=Travel, title=aaa, price=10]]
//Cooking=Optional[Book[category=Cooking, title=ccc, price=50]]
//History=Optional[Book[category=History, title=eee, price=30]]
static <T> Collector<T,?,T> reducing (T identity, BinaryOperator<T> op)
record Book(String category, String title, int price) {
}
final var stream = Stream.of(
new Book("Travel", "aaa", 10),
new Book("History", "bbb", 20),
new Book("Cooking", "ccc", 50),
new Book("Cooking", "ddd", 40),
new Book("History", "eee", 30)
);
final var identity = new Book("", "", 0);
final var comparator = Comparator.comparing(Book::price);
final var map = stream.collect(
Collectors.groupingBy(
Book::category,
Collectors.reducing(
identity,
BinaryOperator.maxBy(comparator)
)));
System.out.println("-- entry --");
for (final var entry : map.entrySet()) {
System.out.println(entry);
}
// 結果
// ↓
//-- entry --
//Travel=Book[category=Travel, title=aaa, price=10]
//Cooking=Book[category=Cooking, title=ccc, price=50]
//History=Book[category=History, title=eee, price=30]
static <T, U> Collector<T,?,U> reducing (U identity, Function<? super T,? extends U> mapper, BinaryOperator<U> op)
record Book(String category, String title, int price) {
}
final var stream = Stream.of(
new Book("Travel", "aaa", 10),
new Book("History", "bbb", 20),
new Book("Cooking", "ccc", 50),
new Book("Cooking", "ddd", 40),
new Book("History", "eee", 30)
);
final var identity = 0;
final var map = stream.collect(
Collectors.groupingBy(
Book::category,
Collectors.reducing(
identity,
Book::price,
BinaryOperator.maxBy(Comparator.naturalOrder())
)));
System.out.println("-- entry --");
for (final var entry : map.entrySet()) {
System.out.println(entry);
}
// 結果
// ↓
//-- entry --
//Travel=10
//Cooking=50
//History=30
static <T> Collector<T,?,DoubleSummaryStatistics> summarizingDouble (ToDoubleFunction<? super T> mapper)
final var stream = Stream.of(1.0, 2.0, 3.0);
final var ret = stream.collect(Collectors.summarizingDouble(value -> value));
// DoubleSummaryStatistics{count=3, sum=6.000000, min=1.000000, average=2.000000, max=3.000000}
System.out.println(ret);
static <T> Collector<T,?,IntSummaryStatistics> summarizingInt (ToIntFunction<? super T> mapper)
final var stream = Stream.of(1, 2, 3);
final var ret = stream.collect(Collectors.summarizingInt(value -> value));
// IntSummaryStatistics{count=3, sum=6, min=1, average=2.000000, max=3}
System.out.println(ret);
static <T> Collector<T,?,LongSummaryStatistics> summarizingLong (ToLongFunction<? super T> mapper)
final var stream = Stream.of(10000000000L, 20000000000L, 30000000000L);
final var ret = stream.collect(Collectors.summarizingLong(value -> value));
// LongSummaryStatistics{count=3, sum=60000000000,
// min=10000000000, average=20000000000.000000, max=30000000000}
System.out.println(ret);
static <T> Collector<T,?,Double> summingDouble (ToDoubleFunction<? super T> mapper)
final var stream = Stream.of(1.0, 2.0, 3.0);
final var ret = stream.collect(Collectors.summingDouble(value -> value));
System.out.println(ret); // 6.0
static <T> Collector<T,?,Integer> summingInt (ToIntFunction<? super T> mapper)
final var stream = Stream.of(1, 2, 3);
final var ret = stream.collect(Collectors.summingInt(value -> value));
System.out.println(ret); // 6
static <T> Collector<T,?,Long> summingLong (ToLongFunction<? super T> mapper)
final var stream = Stream.of(10000000000L, 20000000000L, 30000000000L);
final var ret = stream.collect(Collectors.summingLong(value -> value));
System.out.println(ret); // 60000000000
static <T, R1, R2, R> Collector<T,?,R> teeing (Collector<? super T,?,R1> downstream1, Collector<? super T,?,R2> downstream2, BiFunction<? super R1,? super R2,R> merger)
final var stream = Stream.of("aaa", "bbb", "ccc");
final var ret = stream.collect(Collectors.teeing(
Collectors.joining("-"),
Collectors.joining("+"),
(s1, s2) -> "[" + s1 + ", " + s2 + "]"
));
System.out.println(ret); // [aaa-bbb-ccc, aaa+bbb+ccc]
static <T, C extends Collection<T>> Collector<T,?,C> toCollection (Supplier<C> collectionFactory)
final var stream = Stream.of("aaa", "bbb", "ccc");
final var ret = stream.collect(Collectors.toCollection(LinkedList::new));
System.out.println(ret); // [aaa, bbb, ccc]
System.out.println(ret.getClass()); // class java.util.LinkedList
static <T, K, U> Collector<T,?,ConcurrentMap<K,U>> toConcurrentMap (Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper)
record Item(String name, int value) {
}
final var stream = Stream.of(
new Item("aaa", 10),
new Item("bbb", 20),
new Item("ccc", 30)
);
final var ret = stream.collect(Collectors.toConcurrentMap(
Item::name, Item::value));
System.out.println(ret); // {aaa=10, ccc=30, bbb=20}
static <T, K, U> Collector<T,?,ConcurrentMap<K,U>> toConcurrentMap (Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper, BinaryOperator<U> mergeFunction)
record Item(String name, int value) {
}
final var stream = Stream.of(
new Item("aaa", 1),
new Item("aaa", 2),
new Item("bbb", 30),
new Item("bbb", 40),
new Item("ccc", 500)
);
final var ret = stream.collect(Collectors.toConcurrentMap(
Item::name, Item::value, (value1, value2) -> value1 + value2));
System.out.println(ret); // {aaa=3, ccc=500, bbb=70}
static <T, K, U, M extends ConcurrentMap<K, U>> Collector<T,?,M> toConcurrentMap (Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper, BinaryOperator<U> mergeFunction, Supplier<M> mapFactory)
record Item(String name, int value) {
}
final var stream = Stream.of(
new Item("aaa", 1),
new Item("aaa", 2),
new Item("bbb", 30),
new Item("bbb", 40),
new Item("ccc", 500)
);
final var ret = stream.collect(Collectors.toConcurrentMap(
Item::name, Item::value, (value1, value2) -> value1 + value2,
ConcurrentSkipListMap::new));
System.out.println(ret); // {aaa=3, bbb=70, ccc=500}
System.out.println(ret.getClass()); // class java.util.concurrent.ConcurrentSkipListMap
static <T> Collector<T,?,List<T>> toList ()
final var stream = Stream.of("aaa", "bbb", "ccc");
final var ret = stream.collect(Collectors.toList());
System.out.println(ret); // [aaa, bbb, ccc]
// 変更可能です。
ret.add("XXX");
System.out.println(ret); // [aaa, bbb, ccc, XXX]
static <T, K, U> Collector<T,?,Map<K,U>> toMap (Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper)
record Item(String name, int value) {
}
final var stream = Stream.of(
new Item("aaa", 10),
new Item("bbb", 20),
new Item("ccc", 30)
);
final var ret = stream.collect(Collectors.toMap(
Item::name, Item::value));
System.out.println(ret); // {aaa=10, ccc=30, bbb=20}
// 変更可能です。
ret.put("XXX", 999);
System.out.println(ret); // {aaa=10, ccc=30, bbb=20, XXX=999}
static <T, K, U> Collector<T,?,Map<K,U>> toMap (Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper, BinaryOperator<U> mergeFunction)
record Item(String name, int value) {
}
final var stream = Stream.of(
new Item("aaa", 1),
new Item("aaa", 2),
new Item("bbb", 30),
new Item("bbb", 40),
new Item("ccc", 500)
);
final var ret = stream.collect(Collectors.toMap(
Item::name, Item::value, (value1, value2) -> value1 + value2));
System.out.println(ret); // {aaa=3, ccc=500, bbb=70}
// 変更可能です。
ret.put("XXX", 999);
System.out.println(ret); // {aaa=3, ccc=500, bbb=70, XXX=999}
static <T, K, U, M extends Map<K, U>> Collector<T,?,M> toMap (Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper, BinaryOperator<U> mergeFunction, Supplier<M> mapFactory)
record Item(String name, int value) {
}
final var stream = Stream.of(
new Item("aaa", 1),
new Item("aaa", 2),
new Item("bbb", 30),
new Item("bbb", 40),
new Item("ccc", 500)
);
final var ret = stream.collect(Collectors.toMap(
Item::name, Item::value, (value1, value2) -> value1 + value2,
TreeMap::new));
System.out.println(ret); // {aaa=3, bbb=70, ccc=500}
System.out.println(ret.getClass()); // class java.util.TreeMap
static <T> Collector<T,?,Set<T>> toSet ()
final var stream = Stream.of("aaa", "bbb", "ccc");
final var ret = stream.collect(Collectors.toSet());
System.out.println(ret); // [aaa, ccc, bbb]
// 変更可能です。
ret.add("XXX");
System.out.println(ret); // [aaa, ccc, bbb, XXX]
static <T> Collector<T,?,List<T>> toUnmodifiableList ()
final var stream = Stream.of("aaa", "bbb", "ccc");
final var ret = stream.collect(Collectors.toUnmodifiableList());
System.out.println(ret); // [aaa, bbb, ccc]
ret.add("XXX"); // java.lang.UnsupportedOperationException
static <T, K, U> Collector<T,?,Map<K,U>> toUnmodifiableMap (Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper)
record Item(String name, int value) {
}
final var stream = Stream.of(
new Item("aaa", 10),
new Item("bbb", 20),
new Item("ccc", 30)
);
final var ret = stream.collect(Collectors.toUnmodifiableMap(
Item::name, Item::value));
System.out.println(ret); // {aaa=10, ccc=30, bbb=20}
ret.put("XXX", 999); // java.lang.UnsupportedOperationException
static <T, K, U> Collector<T,?,Map<K,U>> toUnmodifiableMap (Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper, BinaryOperator<U> mergeFunction)
record Item(String name, int value) {
}
final var stream = Stream.of(
new Item("aaa", 1),
new Item("aaa", 2),
new Item("bbb", 30),
new Item("bbb", 40),
new Item("ccc", 500)
);
final var ret = stream.collect(Collectors.toUnmodifiableMap(
Item::name, Item::value, (value1, value2) -> value1 + value2));
System.out.println(ret); // {aaa=3, ccc=500, bbb=70}
ret.put("XXX", 999); // java.lang.UnsupportedOperationException
static <T> Collector<T,?,Set<T>> toUnmodifiableSet ()
final var stream = Stream.of("aaa", "bbb", "ccc");
final var ret = stream.collect(Collectors.toUnmodifiableSet());
System.out.println(ret); // [aaa, ccc, bbb]
ret.add("XXX"); // java.lang.UnsupportedOperationException