0
0

More than 1 year has passed since last update.

Java-StreamAPI-黒本で間違えた問題を理解する

Posted at

前置き

JavaGold資格取得のため、黒本4章でStreamAPIで主が間違えた問題を勉強していきます。
今回は自分のためのほぼメモなので読めたもんじゃないです。

問 OptionalクラスのifPresentOrElseTestメソッドに渡す引数の型の組み合わせとして正しいものを選べ

「型の組み合わせ?知るか!」
「ifPresentOrElseメソッドじゃなくて?ifPresentOrElse"Test"メソッド!?」

...解説見たら一応納得できた
以下サンプルコード。

import java.util.Optional;

public class ifPresentOrElseTest {
    public static void main(String[] args) {
        Optional<String> sample = Optional.empty();
        sample.ifPresentOrElse(
            (str) -> System.out.println(str), //値がある場合、ラムダ式(Consumer型)
            () -> System.out.println("empty");//値がない場合、ラムダ式(Runnable型)
    }
}

答え

Consumer,Runnable
※結局Oracleの公式ドキュメントでもifPresentOrElseメソッドしかなく、
ifPresentOrElseTestメソッドはありませんでした...黒本のミス?

「信じられるのは公式ドキュメントだら」

String型要素を扱うjava.util.List型変数「list」から並列ストリームを取得するコードとして正しいものを選びなさい。

「並列ストリーム!?初耳ですけど!?」

以下、サンプルコード

import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class ParallelTest {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("A","B","C","D","E");
        Stream<String> stream = list.parallelStream();
        stream.forEach(System.out::println);
    }
}

実行結果
image.png

「なるほど、parallelStreamを使うだけで並列で処理してくれるってことね」

問 次のプログラムをコンパイル、実行したときの結果として、正しいものを選びなさい

public class Value {
    private String data;
    public Value(String data) {
        this.data = data;
    }
    @Override
    public int hashCode() {
        return 100;
    }
    @Override 
    public boolean equals(Object obj) {
        return true;
    }
}
import java.util.Arrays;
import java.util.List;

public class Test {
    public static void main(String[] args){
        List<Value> list = Arrays.asList(
            new Value("A"),
            new Value("B"),
            new Value("C"),
            new Value("A")
        };
        long size = list.stream().distinct().count();
        System.out.println(size);
    }
}

解答

「1」と表示される

「distinctでList重複なしで抽出しているから3か.....え!?"1"!?」

重複しているかどうかはequalsメソッドの結果がtrueになるかどうかによって判定されます

「あーValueクラスでequalsメソッドをオーバーライドして常にtrueを返すようにしているからか」
「ん?じゃあ、オーバーライドするのはequalsメソッドだけでhashCodeメソッドもする必要あるのか?」

オーバーライドするのをequalsメソッドだけ、またはhashCodeメソッドだけにしたところ、結果は"4"で重複を除くことはできませんでした...なんで?」

公式ドキュメントを見てみました

通常、このメソッドをオーバーライドする場合は、hashCodeメソッドを常にオーバーライドして、「等価なオブジェクトは等価なハッシュ・コードを保持する必要がある」というhashCodeメソッドの汎用規約に従う必要があることに留意してください。

「つまりequalsメソッドはhashCoddeメソッドで同じhashCodeであるかも見ているってことか..」

問 次のプログラムの「/* insert code here */」に入るコードとして、最も適切なものを選びなさい

List<Integer> list = Arrays.asList(1,2,3,4,5)
/* insert code here */ result = list.stream().reduce(0,(a,b) -> a + b);

「reduceメソッドはたしかOptional型を返すだら?」
「答えはOptionalだ!」

解答

int

「...え!?!?」

なお初期値を受け取らないredduceメソッドの戻り値型はOptionalでしたが、この初期値を受け取るタイプのreduceメソッドの戻り値型は、初期値として渡したデータ型と同じになります

「....くそが!」

資格勉強ってこういう細かいところも覚えなきゃいけなくてつらいですよね

あ、ちなみに実行結果は順番に足されて行って15となります。

問 java.util.stream.Collectorインターフェースに規定されているメソッドの内、実際に行いたい処理を記述するためのものを選びなさい

「Collectorインターフェースがそもそもわからないのですが...?」

###Collectorインターフェースを実現したクラスの定義

Collectorには、supplier,accumulator,combiner,finisher,characteristicsという5つの抽象メソッドがあり、これらすべてを実装しなければなりません。

「え!?全部!?めんどくさ!」「こんな面倒くさいのいつ使うんだよ...」

メソッド 目的
supplier 処理途中の値を保持するためのオブジェクトを生成するメソッド
accumurator 具体的に実行したい処理を記述したBiConsumer型のラムダ式を戻すメソッド
combiner 並列処理をしているときにこのに作られた処理途中の値を保持するためのオブジェクトを結合する
finisher 処理結果を戻すラムダ式を提供するメソッド
characteristics Collecoterの特徴を表すEnumのセットを戻すメソッド

「うーん、よくわからない。特にcombinerとcharacteristics。」

「とりあえず、サンプルコード実行して理解してみる」

import java.util.EnumSet;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;

public class SampleCollector implements Collector<String, StringBuilder, String> {

	@Override
	public Supplier<StringBuilder> supplier() { //処理途中を値をStringBuilder型で保持する
		return StringBuilder::new;
	}

	@Override
	public BiConsumer<StringBuilder, String> accumulator() { //具体的に実行したい操作
		return (builder, str) -> {
			if(builder.length() != 0) {
				builder.append(",");
			}
			builder.append(str);  //カンマを付けてStringBuilderに格納する
		};
	}

	@Override
	public BinaryOperator<StringBuilder> combiner() {
		return null;  
	}

	@Override
	public Function<StringBuilder, String> finisher() {//処理結果を戻す
		return builder -> builder.toString();  //StringBuilderを受け取りStringに変換して返す
	}

	@Override
	public Set<Characteristics> characteristics() {
		return EnumSet.noneOf(Characteristics.class);
	}
	
}

「いや、サンプル長すぎじゃん!」

とりあえず、combinerは置いといて、「characteristics」について
Collectorの特徴を表すEnumのセットを戻すメソッドらしい

【Collector.Characteristicsの列挙子】

列挙子 概要
CONCURRENT このCollectorが並行処理をすることを表す
IDDENTITY_FINISH このCollectorのfinisherメソッドが省略可能であることを表す
UNORDERED コレクションの操作において順序の維持を保証しないことを表す

もしこれらを指定する必要がなければ、java.util.EnumSetクラスのnoneOfメソッドにCollector.Characteristicsクラスのクラスリテラル(クラスの定義情報を表すリテラル)を渡せば、Collector.Characteristics型を扱う空のsetオブジェクトが生成されます

「結局よくわからんな。とりあえず深みにはまる前にCollectorインターフェースを実現したクラスの必ず実装しなければならないメソッドに"characteristics"という長くて読みにくいメソッドがある。このメソッドはCollectorの特徴を表すEnumのセットを戻すけど、よくわからない人は以下のように書いておけばいい」と覚えていったん置いておこう

@Override 
public Set<Characteristics> characteristics() {
    return EnumSet.noneOf(Characteristics.class);
}

「combinerも並列処理の時に考えなければいけないメソッドとだけ覚えておこう」

「...え?適当すぎないかって?」
「資格勉強の第一目標は受かることだから深みにハマる前に先に進むのです!」

一旦ここまで

0
0
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
0
0