ちょっと前にjava7のサポートが終了しましたね。
そんな時代でも現場の方は まだまだjava8なんて安定もしてないし使えないよ。と言う方もいるのではないでしょか?
(「java8 利用率」でググると残念な結果が出ますしね。)
もしjava8にしたとしてもラムダ禁止・StreamAPI禁止とかありがちですよね。
「ラムダもStreamAPIも使わないでjava8使ってる意味あるの?」って言われそうですが
ラムダとかStreamAPIとか使わなくたってjava8はとても素晴らしんですよ!ってのが言いたくていくつか投稿しようと思ってます。
前置きが長くなりましたが、紹介していきます。
#Map#computeIfAbsent
Mapに要素がなければ追加して返すやつです。
これが結構使えると思ってます。
たとえば、
public static class Foo {
private final String key1;
private final String key2;
public Foo(String key1, String key2) {
this.key1 = key1;
this.key2 = key2;
}
public String getKey1() {
return this.key1;
}
public String getKey2() {
return this.key2;
}
@Override
public String toString() {
return "{key1=" + this.key1 + ", key2=" + this.key2 + "}";
}
}
private static final Map<String, Map<String, Foo>> CACHE = new ConcurrentHashMap<>();
public static Foo createFoo(String key1, String key2) {
//DBアクセスやらする想定
return new Foo(key1, key2);
}
/**
* キャッシュを検索してなければ新しくインスタンス作って返す
*/
public static Foo get(String key1, String key2) {
Map<String, Foo> map = CACHE.get(key1);
if (map == null) {
map = new HashMap<>();
CACHE.put(key1, map);
}
Foo foo = map.get(key2);
if (foo == null) {
foo = createFoo(key1, key2);
map.put(key2, foo);
}
return foo;
}
っていう。キャッシュするようなロジックよく書きませんか?(僕は書いてましたよ)
これがjava8のMap#computeIfAbsentを使う(ラムダ使わない)と
public static Foo get(String key1, String key2) {
return CACHE.computeIfAbsent(key1, new Function<String, Map<String, Foo>>() {
@Override
public Map<String, Foo> apply(String t) {
return new HashMap<>();
}
}).computeIfAbsent(key2, new Function<String, Foo>() {
@Override
public Foo apply(String t) {
return createFoo(key1, key2);
}
});
}
っとまあ@Overrideアノテーションがあるので行数変わりませんが、アノテーション分を抜けば2行減りますし、
実質、null判定とputは自力で書いてないのと一緒なので労力的には4行分減ったと思います。
これだけでもjava8使う意味はありますよね。
ちなみにラムダで書けば
public static Foo get(String key1, String key2) {
return CACHE.computeIfAbsent(key1, key -> new HashMap<>())
.computeIfAbsent(key2, key -> createFoo(key1, key2));
}
っと超スッキリ。
#List#sortとComparator追加メソッド
ソートです。
そもそもわざわざCollectionsをimportしなくてよくなっただけでも僕は喜んでいますが、
以前は、
public static class Foo {
private final int value;
public Foo(int value) {
this.value = value;
}
public int getValue() {
return value;
}
@Override
public String toString() {
return String.valueOf(value);
}
}
/**
* ソート
*/
public static void sort() {
// getValue()でソート
List<Foo> list = new ArrayList<>(Arrays.asList(new Foo(3), new Foo(2), new Foo(1)));
Collections.sort(list, new Comparator<Foo>() {
@Override
public int compare(Foo o1, Foo o2) {
return Integer.compare(o1.getValue(), o2.getValue());
}
});
System.out.println("sort:" + list);
// nullを最後にするソート
List<Foo> listIncNull = new ArrayList<>(Arrays.asList(new Foo(3), new Foo(2), null, new Foo(1)));
Collections.sort(listIncNull, new Comparator<Foo>() {
@Override
public int compare(Foo o1, Foo o2) {
if (o1 == null) {
return (o2 == null) ? 0 : 1;
} else if (o2 == null) {
return -1;
} else {
return Integer.compare(o1.getValue(), o2.getValue());
}
}
});
System.out.println("null last sort:" + listIncNull);
}
っとこんな感じで、
普通のソートでもCollections#sort使うし、
nullが入ってきたらめんどくさいしでしたね。
これがjava8(ラムダなし)だと
public static void sort() {
// getValue()でソート
List<Foo> list = new ArrayList<>(Arrays.asList(new Foo(3), new Foo(2), new Foo(1)));
list.sort(Comparator.comparingInt(new ToIntFunction<Foo>() {
@Override
public int applyAsInt(Foo value) {
return value.getValue();
}
}));
System.out.println("sort:" + list);
// nullを最後にするソート
List<Foo> listIncNull = new ArrayList<>(Arrays.asList(new Foo(3), new Foo(2), null, new Foo(1)));
listIncNull.sort(Comparator.nullsLast(Comparator.comparingInt(new ToIntFunction<Foo>() {
@Override
public int applyAsInt(Foo value) {
return value.getValue();
}
})));
System.out.println("null last sort:" + listIncNull);
}
っと普通のソートではgetValueを2回書かなくていいのがメリットかと思います。
(もし別のgatほげほげがあった場合間違えたりしない)
そしてnullを最後にしたいときわざわざ自力で書かなくてもComparator#nullsLastでOKになります。1
ちなみにラムダ2だと
public static void sort() {
// getValue()でソート
List<Foo> list = new ArrayList<>(Arrays.asList(new Foo(3), new Foo(2), new Foo(1)));
list.sort(Comparator.comparingInt(Foo::getValue));
System.out.println("sort:" + list);
// nullを最後にするソート
List<Foo> listIncNull = new ArrayList<>(Arrays.asList(new Foo(3), new Foo(2), null, new Foo(1)));
listIncNull.sort(Comparator.nullsLast(Comparator.comparingInt(Foo::getValue)));
System.out.println("null last sort:" + listIncNull);
}
っと超スッキリ。
はい。僕はラムダ大好きです。
その2に続く。