Java8が好きになる話（StreamAPIの話はしない） その1 Map#computeIfAbsentとList#sort

ちょっと前にjava7のサポートが[終了](https://java.com/ja/download/faq/java_7.xml)しましたね。
そんな時代でも現場の方は まだまだjava8なんて[安定もしてないし](http://www.ne.jp/asahi/hishidama/home/tech/java/methodreference.html#h_constructor_reference_local_class)使えないよ。と言う方もいるのではないでしょか？
（「[java8 利用率」でググると](https://www.google.co.jp/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=java8+%E5%88%A9%E7%94%A8%E7%8E%87)残念な結果が出ますしね。）
もしjava8にしたとしてもラムダ禁止・StreamAPI禁止とかありがちですよね。

「ラムダもStreamAPIも使わないでjava8使ってる意味あるの？」って言われそうですが
ラムダとかStreamAPIとか使わなくたってjava8はとても素晴らしんですよ！ってのが言いたくていくつか投稿しようと思ってます。

前置きが長くなりましたが、紹介していきます。
#Map#computeIfAbsent
Mapに要素がなければ追加して返すやつです。
これが結構使えると思ってます。
たとえば、

```java:準備
	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);
	}

```
```java:java7以前
	/**
	 * キャッシュを検索してなければ新しくインスタンス作って返す
	 */
	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を使う（ラムダ使わない）と

```java:java8ラムダなし
	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使う意味はありますよね。

ちなみにラムダで書けば

```java: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しなくてよくなっただけでも僕は喜んでいますが、
以前は、

```java:準備
	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);
		}
	}
```
```java:java7以前
	/**
	 * ソート
	 */
	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（ラムダなし）だと

```java: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]だと

```java: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(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](http://qiita.com/ota-meshi/items/dbdd592b613e38b7a4bb)に続く。

[^1]: nullsFirstってもあります。
[^2]: というかメソッド参照