JEP 269: Convenience Factory Methods for Collections
コレクションへの便利なファクトリメソッドを追加します、と。
上記のページにはJavaでのコレクション生成についてのJDK 9以前の書き方がいろいろありました。
一番ベーシックなやつ。
Set<String> set = new HashSet<>(); set.add("a"); set.add("b"); set.add("c"); set = Collections.unmodifiableSet(set);
Arrays.asList()を使うパターン。
Set<String> set = Collections.unmodifiableSet(new HashSet<>(Arrays.asList("a", "b", "c")));
これはしたことなかった。 "double brace" テクニックと呼ばれているそうです。「it costs an extra class at each usage」と、使うたびにクラスを追加するので、余計なコストはかかりますね。
Set<String> set = Collections.unmodifiableSet(new HashSet<String>() {{ add("a"); add("b"); add("c"); }});
Java 8ならStream APIでこう書けるぜ!でも正直やりません。
Set<String> set = Collections.unmodifiableSet(Stream.of("a", "b", "c").collect(toSet()));
それが!JDK 9ではこう書けるぞ!うん、これぐらいがいいよね。これがJava的にも限界かな。
Set<String> set = Set.of("a", "b", "c");
さっそく試してみましょう。JDK™ 9 Early Access Releasesを使うと楽チンです。
https://jdk9.java.net/download/
せっかくなんで、JDK 9で導入される、JavaのREPLツール「jshell」でやりましょう。
$ java -version java version "9-ea" Java(TM) SE Runtime Environment (build 9-ea+99-2015-12-23-184955.javare.4146.nc) Java HotSpot(TM) 64-Bit Server VM (build 9-ea+99-2015-12-23-184955.javare.4146.nc, mixed mode) $ jshell | Welcome to JShell -- Version 9-ea | Type /help for help -> Set.of(1,2,3) | Expression value is: [1, 2, 3] | assigned to temporary variable $1 of type Set<Integer> -> Set set = Set.of(1,2,3,4) | Added variable set of type Set with initial value [1, 2, 3, 4] -> set.size() | Expression value is: 4 | assigned to temporary variable $3 of type int
おおー、Set.of()で要素ありのSetが作れました。このSetってクラスは何でしょうね?
-> set.getClass() | Expression value is: class java.util.Collections$UnmodifiableSet | assigned to temporary variable $2 of type Class<? extends Set>
UnmodifiableSetですね。
ListもMapもof()で作れます。
-> List list = List.of("a", "b", "c") | Added variable list of type List with initial value [a, b, c] -> list.getClass() | Expression value is: class java.util.Collections$UnmodifiableRandomAccessList | assigned to temporary variable $4 of type Class<? extends List>
ListはUnmodifiableRandomAccessListでした。 Mapはこうです。
-> Map map = Map.of("hoge", 1, "fuga", 2, "java", 3) | Added variable map of type Map with initial value {fuga=2, java=3, hoge=1} -> map.getClass() | Expression value is: class java.util.Collections$UnmodifiableMap | assigned to temporary variable $6 of type Class<? extends Map>
ちょっとくすぐったい感じがします。of(key1, value1, key2, value2, key3, value3)と記述します。もし奇数個だったら…
-> Map.of("hoge", 1, "fuga", 2, "java") | Error: | ofに適切なメソッドが見つかりません(java.lang.String,int,java.lang.String,int,java.lang.String) | メソッド java.util.Map.<K,V>of()は使用できません | (型変数K,Vを推論できません | (実引数リストと仮引数リストの長さが異なります)) | メソッド java.util.Map.<K,V>of(K,V)は使用できません | (型変数K,Vを推論できません | (実引数リストと仮引数リストの長さが異なります)) | メソッド java.util.Map.<K,V>of(K,V,K,V)は使用できません | (型変数K,Vを推論できません | (実引数リストと仮引数リストの長さが異なります)) | メソッド java.util.Map.<K,V>of(K,V,K,V,K,V)は使用できません | (型変数K,Vを推論できません | (実引数リストと仮引数リストの長さが異なります)) | メソッド java.util.Map.<K,V>of(K,V,K,V,K,V,K,V)は使用できません | (型変数K,Vを推論できません | (実引数リストと仮引数リストの長さが異なります)) | メソッド java.util.Map.<K,V>of(K,V,K,V,K,V,K,V,K,V)は使用できません | (型変数K,Vを推論できません | (実引数リストと仮引数リストの長さが異なります)) | メソッド java.util.Map.<K,V>of(K,V,K,V,K,V,K,V,K,V,K,V)は使用できません | (型変数K,Vを推論できません | (実引数リストと仮引数リストの長さが異なります)) | メソッド java.util.Map.<K,V>of(K,V,K,V,K,V,K,V,K,V,K,V,K,V)は使用できません | (型変数K,Vを推論できません | (実引数リストと仮引数リストの長さが異なります)) | メソッド java.util.Map.<K,V>of(K,V,K,V,K,V,K,V,K,V,K,V,K,V,K,V)は使用できません | (型変数K,Vを推論できません | (実引数リストと仮引数リストの長さが異なります)) | メソッド java.util.Map.<K,V>of(K,V,K,V,K,V,K,V,K,V,K,V,K,V,K,V,K,V)は使用できません | (型変数K,Vを推論できません | (実引数リストと仮引数リストの長さが異なります)) | メソッド java.util.Map.<K,V>of(K,V,K,V,K,V,K,V,K,V,K,V,K,V,K,V,K,V,K,V)は使用できません | (型変数K,Vを推論できません | (実引数リストと仮引数リストの長さが異なります)) | Map.of("hoge", 1, "fuga", 2, "java") | ^----^
すごく怒られました。Mapにはof()だけでなくMap.ofEntries(Map.Entry<K,V>...)というのもあります。Entryを作るのは、Map.entry(K k, V v)というstaticメソッドが便利です。
-> Map.ofEntries(Map.entry("hoge",1), Map.entry("fuga", 2)) | Expression value is: {hoge=1, fuga=2} | assigned to temporary variable $8 of type Map<String,Integer>
ちょっとしたコレクションは、断然作りやすくなりそうですね。