リスト内に一定の条件に属する要素が存在するか否かを調べる
例えばIntの配列の中に特定の条件を満たす要素が存在しているかどうかを調べたいとする。 今回はそんなときに使える標準関数を紹介する。
例えばIntの配列の中に特定の条件を満たす要素が存在しているかどうかを調べたいとする。 今回はそんなときに使える標準関数を紹介する。
KotlinにgroupingByなる関数があることを知った。
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/grouping-by.html
Groupingソースなるものを作成するための拡張関数で、listとか配列で使うことができる。
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-grouping/index.html
例えば”I have a pen”で各アルファベットが何回出現しているかを調べるのに使える。
val result = "I have a pen".groupingBy { it }.eachCount()
println(result) // {i=1, =3, h=1, a=2, v=1, e=2, p=1, n=1}
groupingBy自体は続く関数オブジェクトで求められるkeyを元にしたMapへ集計できるようにするためのインターフェースで、これ自体呼び出しても何も起こらない。上記の例でいうとeachCount()を呼び出して初めて集計が行われる。
keySelectorを引数に取るのはgroupByもgroupingByも同じ。
groupByだと指定したkeyごとの要素をListにもつMapを返す。イメージ的にはmapに近い処理。
groupingByはそれ自体は何もしない。keyを元に集計処理を行うインターフェースを用意するだけのメソッドなので、その後に別途集計処理が必要になる。forEachを拡張したものと考えるといいかもしれない。
両者の使い分けは、keyを元にした要素のリストがほしいのか、それともその要素を何らかの処理をして集計した結果だけが欲しいのかで使い分けることになるだろう。集計した結果のみが必要なのであれば、その中間形態であるkeyごとの要素リストは不要なので、groupingByを使ったほうが効率的である。
groupingByだけでは集計処理は行われないので、その後に以下のいずれかを利用して集計を行う。
それぞれ別にxxxToという処理も用意されていて、違いは集計先のMapが指定できるかどうか。Toがついている方は、既存のMapが集計先に利用されるので、前の集計結果にさらに付け足すのに使える。Toがつかない方は空のMapが集計先として利用される。
groupingBy自体は要素のグルーピングを行うわけではなく、aggregateなどを呼び出すことで初めて要素のグルーピングと集計が行われる。
よほど特殊な事情がない限り、aggregateを直接使うことはないと思われる。大体のケースでfoldを使ったほうが便利だろう。
というのも、aggregateは要素がkeyによるグルーピングを行った最初の要素かどうかを判定したりするのも全て自分で書く必要があるからだ。要素が初出の場合に初期値を用意し、そうでない場合に集計処理をするというのがfoldなので、大抵のケースでfoldで事足りるはず。
最終的にはどれを使ってもList<何らかの型>がMap<指定したキー, 集計後の結果>に集計される。(元のデータがListとは限らないけれど、最終的にMapに集約されるのは変わらない)
keyごとの要素の個数が欲しい場合にこれを使う。引数もいらないので最もシンプル。
KotlinでUnitテストを書く際に、assertionに何を使うかという話。
私の場合特にこだわりがあるわけではないのだが、基本的には多数派に従いたいという気持ちが強い。しかしながら、Kotlinのテストのassertionに使うならこれ一択、みたいなものがない(と思っている)ので、いつも困る。
Javaなら特に何もせずともhamcrestのassertThat(sut.getHoge()).is("fuga")みたいなassertionを使っていればいいのだが、Kotlinの場合はisが予約語であるという問題がある。いちいちisをバッククォートでくくらなければならず美しくない。
日本ではいちばん有名なやつだと思われる(DroidKaigiのアプリでも使われていたはずなので)。
sut.getHoge().should be "fuga"みたいな感じで使う。
ただrepositoryを追加しないと組み込めないので私はあまり使っていない。build.gradleに一行追加するだけの話なんだけどね・・・。
https://github.com/winterbe/expekt
knitに似たsut.getHoge().should.equals("fuga")みたいな感じのAPI。リポジトリの追加が必要ないので、私はこっちをよく使っている。
ただメンテされてないのが玉に瑕。
https://github.com/kotlintest/kotlintest
assertionライブラリではなくて、Spec風のテストを書くためのライブラリ。
ただsut.getHoge() shouldBe "fuga"みたいなassertionが含まれている。
SpekというSpec風のテストを書くためのライブラリがあるが、こちらはJava8でなければ使えないという制約があって、Androidのプロジェクトに適用するのはなんだか難しそうで手を出していない。
kotlintestはネストしたテストを書きやすくて、これいいかもとか思ったこともあったのだけれど、androidTestに組み込むとメソッドカウントがオーバーしてしまうので使えない。
https://github.com/willowtreeapps/assertk
Android Weeklyで流れてきて知った。AssertJチックなassertionライブラリで、Kotlinで使うならコレのが便利なのだろうか。
assert(sut.getHoge()).isEqualTo("fuga")みたいな感じで使う。
しかし個々最近はshould系ばかり使ってきているので、最初にassertを書かないといけない形式はめんどくさいと感じてしまっている。
https://joel-costigliola.github.io/assertj/
Javaのコードも考慮に入れるならJavaで使えるライブラリを使うのがいいのだろう。
私はJavaだとhamcrest使っとけばいいやな人だったので、Javaでassersionライブラリを何使うかなんて特に気にしたことはなかった。
みんなはどれを使っているのだろうか。他にこれが使いやすいみたいなのがあったら教えて欲しい。
ちなみにこの記事を書いたのはかなり昔のことなので、今はだいぶ環境が変わっている。マシンスペックによる辛さも含まれていたので、現在ではだいぶ解消されていると思って読んで欲しい。
なにせ3年もまえの記事だからね・・・あんまり真に受けないように。19年時点の私はだいぶKotlinラバーですから。
最近アプリやAndroid Studio用プラグインを作るのにKotlinを使っています。
始めたばかりの頃は「Javaだとああ書くんだけど、Kotlinだとどう書けばいいんだ」ということが多かったです。Javaでまともに書けないのにKotlinに手を出すのは早いんじゃないかとも思っていました。
しかし少しずつ試していくと、Kotlinの便利な部分が分かってきてきました。
私の場合、「Kotlinで書き始めたんだけどやっぱ使い方よくわからないからJavaに戻そう」とう場面が初期の頃はよくありました。はじめはKotlinで書いていたけど、やっぱりJavaで実装しようとという感じです。
そんなときに、「Javaに戻すのめんどくせえ」と感じる部分があって、そこで改めて「Kotlinってやっぱ便利やなぁ」なんて実感しました。
それからというもの、Kotlinの比重が徐々に増えてきて、今では逆にJavaで書く方が面倒くさいと感じるようになってしまいました。
一方で、Kotlinが無敵というわけではありません。Annotation Processingを使うライブラリがKotlinだとうまく使えないことがあったり(基本的には大丈夫ですが、Javaで書けば動くコードがKotlinで同じように書くと動かないことがあったりします)、Javaと比べるとコード補完が遅かったり、Kotlinを使うことで感じるストレスもあります。
ですが、不便さを差し置いてもKotlinで書いた方がすっきり書けるのはやっぱり快適だと思っています。
どなたかの記事で、Kotlinはテストコードから導入してみたらどうかという記事を読みました。私もいい方法だと思います。Kotlinの便利さを実感するためではなく、どう書くかを知るのにちょうどいいと思います。
私もJUnitでのユニットテストをKotlinで書いています。テスト対象をKotlinで書いてるからとか、セミコロンつけなくてもいいから、とかそんな理由です。ユニットテストについてはKotlinが便利だからという理由はあまりないかもしれません。
ユニットテストにおいてKotlinが便利だと思うのは、バッククオート(`)で囲むことで、メソッド名やクラス名を数字から始めることができたり、途中に空白を含めることができたりすることでしょうか。
私はテスト名に日本語を使うことが多いです。そしてそのときに、メソッド名に使える文字に制約があるのが微妙に困ります。
例えば各月の最終日を求めるメソッドのテストをするのに「4月の場合は30日を返す」というテスト名にしたくてもできません。Javaではメソッド名を数字から始めることができないからです。だからこんなときは「月に4月を指定したら30日を返す」という感じのメソッド名にするのですが、これが微妙なストレスになります。(頭に「月を」つけるだけやんという感じですが、微妙にストレス感じるんですよこれ)
Kotlinではこの制約に煩わされることがありません。メソッド名をバッククオートで囲めば、数字から始めようが、途中に空白を挟もうが問題ないのです。
@Test fun `2つの時刻の差を求める`(){
val time1 = LocalDateTime.of(2014, 1, 1, 23, 58, 30)
val time2 = LocalDateTime.of(2014, 1, 2, 0, 4, 30)
val actual = Duration.between(time1,time2).seconds
assertThat(actual, `is`(360L))
}
なにそれ気持ち悪いと思われるかもしれませんが、これはれっきとしたKotlinの仕様です。
Javaのメソッド名規約によってテストメソッド名を考えるのが面倒くさいなぁと感じている人は私だけではないと思いたい。
一方でKotlinでユニットテスト書けば便利なことばかりではありません。例えばassertThatなどを使おうとするとimport文を手書きで書かないと認識してくれないのが不便です。(私の環境の問題なのかもしれません。JavaだとALT+Enterでimportできるんですけどね・・・)
単純なことですが、Kotlinは文末にセミコロンをつけなくてもいいのです。
これが便利・・・と言いたいところですが、私は半々かなぁと感じています。
確かにいちいちセミコロンつけなくてもいいので楽です。たまにJavaでコードを書くときに、しょっちゅうセミコロンつけ忘れます。それくらいには快適です。
一方セミコロンが不要なせいで、メソッドチェーンするときに私は微妙にストレスを感じます。
Javaだとメソッドチェーンするときに改行をするとインデントを一段深くしてくれます。
しかしKotlinでは改行した時に文末なのか次の文に移るのかが判別不能なので、インデントを深くしてくれたりしません。これが毎回微妙にめんどうくさいです。私はいつもドットを打ってカーソル戻して改行するという方法をとって回避しています。
多分Reformat Code(Cmd+Alt+l)を使うのが楽なんでしょうけども。みんなどうしてるんだろう・・・。
Javaだと文字列に変数を埋め込もうと思うと、+演算子で連結しなければなりません。もしくはString.format()を使うかですね。
Kotlinだとそんな面倒くさいことをせずとも、文字列中に変数を埋め込めるので便利です。こんな感じに書けるわけです。