mike-neckのブログ

Java or Groovy or Swift or Golang

JJUG CCC 2019 Spring で 「Collections Framework 入門」というタイトルで発表してきました #jjug_ccc

表題の通り、発表してきました。

www.slideshare.net


いくつか質問を発表後にいただきましたので、ここであらためて回答します

(Q) 資料のコードの中で new ArrayList<>(List.of("foo", "bar")) とやっていますが、これは何のインスタンスを取得していますか?

(A) これは ArrayList (可変な List) のコレクションを取得しています。なお、 Collections Framework の設計方針で、すべての Collection 系のインスタンスコンストラクターには必ず Collection を取れるようにするようにしています。また、発表資料はスペースが限られるので、その中で要素を含む ArrayList を作るコードを書くためには List.of でコレクションを作りながら、 ArrayListコンストラクターを呼び出す方法が1行で書けるので、こちらにしました。

(Q) List.of() によって取得される Listインスタンスの実装クラスは何ですか?

(A) これにはメソッドのパラメーターの数によって実装クラスが変わります。

これと同様に Set の場合は ImmutableCollections.SetNImmutableCollections.Set12 が、 Map の場合は ImmutableCollections.MapNImmutableCollections.Map1(2個の要素の場合は MapN が使われる) があります。


本セッションを応募した理由

複数の Cfp を書きましたが、以前まあやさんや慎さんに「初心者向けのセッションが圧倒的に足りていない」ということを過去に聞いたことがあったので、((基本的すぎるので)需要はないと思うけど)書いたのが今回のテーマでした。

内容

内容については、僕が初学者にわかりやすく説明するなら、こうなるだろうという内容を書いたつもりです。しかし、僕が初学者だったのは十数年前だったので、その頃のことはもう覚えてないし、たとえ覚えていたとしても人間の考え方は人により全く異なるので、正直に言って、僕は作成した資料に対して自身を持てないところがありました。発表の後、ツイッターで反応を見ていたところ、「たとえがよかった」「しっくりきた」「丁寧な説明。基礎知識としてぜひ抑えておきたい」といった反応があり、資料の方向性等で確信を持てました(遅い)。


付録資料について

付録資料の ArrayList/LinkedList の比較は、定期的に話題になるものです。僕も比較してみようと思い、 JMH でコレクションの要素数を細かく設定してパフォーマンス計測をしてみました。

計測用のソースコードはこちらです。もし試してみたい方は、下記のリポジトリーをクローンして、 distZip タスクで計測プログラムをアーカイブ化して、 ec2 などのサーバーで実行することをお勧めします。 Java 12 以上が必要ですが、 11 でも動かせると思います。 Java 8 ではコンパイルできません。

github.com

計測すると、性能の良いマシンでも数時間はかかります。手元のマシンでなく、外部の計算資源をかりて行うほうが良いでしょう。僕は AWS の ec2 の m5a.large インスタンスで実行しました。バースト可能でないインスタンスがよいです。


以上

JMH の細かい実行制御

JMH で何かしらの処理のパフォーマンスを計測する時に、すべての計測対象が必ずしもステートレスにできないケースがある。

例えば、 List<String> にデータが 1万レコードほどある時に、 add(int, String) の呼び出しのパフォーマンスを計測する場合など。

この例の場合だと、 JMH の実行設定をイテレーション10秒に設定していると、 3 億回ほどメソッドが呼び出されたりする。すると、最後の方には List<String> にデータが 3億1 万レコードある状態でのベンチマークとなり、もともと計測したかった 1万レコードの時点での速度計測にならなかったりする。

このようなケースでは、 @Setup@TearDown アノテーションに与える、 Level をうまく設定してやることで、レコード件数を維持できる。

@State(Scope.Group)
public class Sample {

  private List<String> list;

  @Group("sample")
  @Setup(Level.Iteration)
  public void setup() {
    this.list = new ArrayList<>();
    for (int i = 0; i < 10_000; i++) {
      list.add(Util.randomString());
    }
  }

  @Group("sample")
  @Benchmark
  @BenchmarkMode(Mode.Throughput)
  public void addOp() {
    list.add(Util.randomString());
  }

  @Group("sample")
  @TearDown(Level.Invocation)
  public void teardown() {
    list.remove(list.size() - 1);
  }
}

今更な Effective Java 番号1. コンストラクターより static ファクトリーメソッドを選ぶ

今勤めてる企業で質問された際に、うろ覚えで答えたので、もっかい Effective Java 読んだので、そのメモ


番号1. コンストラクターより static ファクトリーメソッドを選ぶ

pros

cons

  • public または protectedコンストラクターを持つクラス以外のサブタイプを作れない
    • が、しかし、大したデメリットではない
  • プログラマーstatic ファクトリーメソッドを見つけられない
    • IDE があるので、それも大したデメリットではない。むしろ、メリットですらあるように思える

Swift の Result にあると良さそうな関数

Swift の Result をいじっていて、書いてしまった拡張関数

f:id:mike_neck:20180609045321p:plain


現在の値を消費して終わりの関数

extension Result {
  func doOn(success: (Success) -> Void, failure: (Failure) -> Void) {
    switch self {
    case .success(let value): success(value)
    case .failure(let error): failure(error)
    }
  }
}

現在の値が .success の場合にデバッグプリントするための関数

extension Result {
  func peek(_ action: (Success) -> Void) -> Result<Success, Failure> {
    let doNothing: () -> Void = {}
    switch self {
    case .success(let value): action(value)
    case .failure(_): doNothing()
    }
    return self
  }
}

現在の値が .failure の場合にデバッグプリントするための関数

extension Result {
  func peekError(_ action: (Failure) -> Void) -> Result<Success, Failure> {
    let doNothing: () -> Void = {}
    switch self {
    case .success(_): doNothing()
    case .failure(let error): action(error)
    }
    return self
  }
}