31
34

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2015-07-10

ちょっと前に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);
	}

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を使う(ラムダ使わない)と

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使う意味はありますよね。

ちなみにラムダで書けば

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);
		}
	}
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(ラムダなし)だと

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だと

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に続く。

  1. nullsFirstってもあります。

  2. というかメソッド参照

31
34
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
31
34

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?