ごあいさつ
こんにちは。meviyのWebシステムを開発している大崎です。
今回は、Scalaのコレクション内に要素の存在チェックに使われるメソッドについて触れたいと思います。
Scalaでコレクションを操作するとき、「該当する要素が存在するか」「特定の条件を満たす要素が存在するか」を確認する場面が出てきます。
その際に使われるのが .contains
および .exists
メソッドです。
本記事では、これらのメソッドの基本的な使い方、実装例、内部実装、そしてJavaでの同等機能との比較を交えつつ、このメソッドを紹介していきます。
各メソッドの紹介
.contains
メソッド
コレクション内に、指定した要素が存在するかを判定するメソッドです。
def contains(elem: Any): Boolean
返り値としては、指定した要素が存在すればtrue、存在しなければfalseとなります。
例を見てみましょう。
val numbers = List(1, 2, 3, 4, 5) val hasThree = numbers.contains(3) println(hasThree) // true
ここでは、リスト内に3が存在するため、trueが返されます。
.exists
メソッド
コレクション内に、指定した条件を満たす要素が1つでも存在するかを判定するメソッドです。
def exists(p: A => Boolean): Boolean
返り値としては、条件を満たす要素があればtrue、なければfalseとなります。
簡単な例を見てみましょう。
val numbers = List(1, 2, 3, 4, 5) val hasEven = numbers.exists(_ % 2 == 0) println(hasEven) // true
ここでは、リスト内に偶数が存在するため、trueが返されます。
.contains
との違いとしては、 .contains
は特定の要素が存在するかを確認するのに対し、 .exists
は条件を満たす要素が存在するかを確認する点です。
引数の型定義を見てみると、 .contains
は要素そのものを引数に取るのに対して、 .exists
は条件を満たす要素を判定するための関数を引数に取ります。
実装例
.contains
メソッドの実装例
まずは、 .contains
を使った実装例として、リスト内に特定の要素が存在するかを確認する例を示します。
val tags = List("Scala", "Java", "Kotlin", "Server-side", "JVM") val hasScala = tags.contains("Scala") println(hasScala) // true
このように、コレクション内に特定の要素が存在するかを確認するのに使えます。
.exists
メソッドの実装例
次に .exists
を使った実装をいくつか示します。
数値リストの条件チェック
val numbers = List(10, 20, 30, 40, 50) val hasNumberGreaterThan25 = numbers.exists(_ > 25) println(hasNumberGreaterThan25) // true
.exists
に渡されている _ > 25
というのは、値が25より大きいかを判定する関数です。
_
は無名関数の引数を表し、ここではリスト内の各要素である、10
や 20
といった値がここに入ります。
.exists
はこの関数を用いてコレクションの各要素をそれぞれ評価し、条件を満たす要素が1つでも存在すればtrueを返します。
.contains
と違い、指定された要素と合致する要素が存在するかではなく、与えられた条件を満たす要素が存在しているかを判定するようになっていることが分かります。
文字列内に特定の単語が存在するかのチェック
val bannedWords = List("spam", "scam", "fake") val inputMessage = "This is a scam message!" val containsBannedWord = bannedWords.exists(inputMessage.contains) println(containsBannedWord) // true
特定の単語が文字列内に存在するかを確認しています。
inputMessage.contains
は、文字列内に特定の文字列が存在するかを判定する関数です。上の例のように引数内で関数を生成するのではなく、既存の関数をそのまま引数に渡すこともできます。
ここでは、禁止ワード3つが文字列内に含まれているかを確認し、どれかが存在していればtrueを返します。
真偽値の確認
case class Task(name: String, completed: Boolean) val tasks = List(Task("Task1", false), Task("Task2", true), Task("Task3", false)) val hasCompletedTask = tasks.exists(_.completed) println(hasCompletedTask) // true
タスクリスト内に「完了済み」のタスクが存在するかを確認しています。
_.completed
は、tasks
の各要素であるTaskクラスのオブジェクトを、そのオブジェクトの completed
フィールドの値に変換する関数です。
.exists
としては、返り値が真偽値である関数を与えられれば問題ないので、このように単にフィールドの値を取り出す関数を渡すこともできます。
内部実装
Scalaの .exists
は短絡評価を採用しており、条件を満たす要素を見つけた時点で処理を終了します。
全ての要素を評価することはないので効率的です。
val largeList = (1 to 1000000).toList val hasLargeNumber = largeList.exists(_ > 999) println(hasLargeNumber) // true
この場合、最初に条件を満たす1000が見つかった時点で探索を終了します。
内部実装を見てみましょう。以下のようになっています。
def exists(p: A => Boolean): Boolean = { var res = false val it = iterator while (!res && it.hasNext) res = p(it.next()) res }
ポイントとしては、イテレータを用いて要素を順次評価し、条件を満たす最初の要素が見つかればwhileループを終了してtrueを返します。全要素を評価しても条件を満たす要素が見つからなければfalseを返します。
FYI:
また、.contains
の実装には内部で .exists
が使われているため、同様に短絡評価が行われます。(以下 Iterator
トレイトの例 )
def contains(elem: Any): Boolean = exists(_ == elem)
Javaでの同等の実装
比較のためにJavaの例を見てみましょう。
まず、.contains
は以下のようになります。
import java.util.List; public class ContainsExample { public static void main(String[] args) { var tags = List.of("Scala", "Java", "Kotlin", "Server-side", "JVM"); var hasScala = tags.contains("Scala"); System.out.println(hasScala); // true } }
比較は Object.equals
メソッドを使って等価比較で行われるため、Scalaの .contains
と同様に指定した要素が存在するかを確認することができます。
こちらは書き味はScalaと同様です。
FYI:
次に、.exists
についてです。
JavaではStream APIの anyMatch
メソッドを使うことで同じような処理が可能です。
こちらもScalaの .exists
と同様に短絡評価が行われるとされています。
FYI:
import java.util.List; public class ExistsExample { public static void main(String[] args) { var numbers = List.of(1, 2, 3, 4, 5); var hasEven = numbers.stream().anyMatch(n -> n % 2 == 0); System.out.println(hasEven); // true } }
比較してみると、Scalaの .exists
はコレクションメソッドとして組み込まれており、シンプルで直感的に使える点が特徴です。
まとめ
Scalaの .exists
メソッドは、指定した条件を満たす要素の有無を判定する処理をシンプルに記述、効率的に実行するのに有用なメソッドとなっております。短絡評価を行うので、条件を満たす要素が存在することが判明した時点で結果を返します。
条件に合致する要素を含んでいるかどうかだけが知りたい場合には、.contains
メソッドを使うことで簡潔に書くことができます。こちらも内部で .exists
をラップしているため、短絡評価が行われます。
終わりに
本記事ではScalaのコレクションの要素の存在チェックについて紹介しました。 他にも、Scalaには様々な機能があり、それらを駆使することでより簡潔にコードを書くことができます。
本ブログでは今後もScalaをはじめとする、DTダイナミクスで用いられている技術やプロダクトなどの魅力を発信していきますので、興味を持っていただけると幸いです。
最後にほかにも、Scalaに関する記事がございますので、ぜひご覧ください。