Javaについて徹底解説!

StringもListもMapもcontainsで! Javaで「含む」をチェックする方法

大石 英人

開発エンジニア/Java20年/Java GOLD/リーダー/ボールド歴2年

String、配列、ListSetMapに、文字列や値、インスタンスが「含まれているか」の確認は、プログラムでは頻繁に行います。

そして、必要な時に限って「えーっと、どうやってやるんだっけ?」となったりしませんか? どうしても、ついつい忘れてしまうと思います。

でも、もう大丈夫です。この記事では、みんながちょっと忘れがちな「何かが何かに含まれているかの調べ方」を、分かりやすくお伝えします!!

※この記事のサンプルは、Java 10の環境で動作確認しています


1.containsは「含んでいるか?」の共通キーワード

プログラミングでは、何かが何かを含んでいるかの確認を良く行います。例えば、文字列に“ERROR”が含まれていればエラー処理をする、数値の配列が異常値を含んでいればエラーとする、などです。

Javaで「含んでいるか?」を知る時には、“contains”という単語を含むメソッドを呼び出して判断します。判断結果は、trueなら含んでいる、falseなら含んでいない、で共通しています。

以下では、String、配列、ListSetなどのCollectionMapが何かを含んでいるかを調べる方法をお伝えします。


2.Stringが含んでいるか?

2-1.String.containsで探す

String.containsは、文字列が引数の文字列を含んでいるか確認するメソッドです。含んでいるならtrue、含んでいなければfalseを戻します。

2-2.String.indexOfで探す

String.containsの代わりに、文字列が最初に出現する位置を戻すindexOfで探してもいいでしょう。

indexOfの戻り値は、文字列中に、探している文字列が出現する位置の数値です(0始まり)。戻り値が-1でなければ、探している文字列を含んでいるということです。

indexOfを使うと、文字列を抽出(substring)したい場合などに便利です。また、indexOfには開始位置を指定できるものや、文字列の後ろから探すものもあります。

このindexOfは、以下の記事でも詳しく説明していますので、ぜひ参考にしてください。

関連記事

2-3.String.matchesで探す

String.matchesを使うと「正規表現」での検索ができます。ですので、String.matchesは「含んでいるか」にも使えたりします。

正規表現と呼ばれる機能を使うと、文字列の検索を効率化できます。正規表現は少し難しいですが、一度覚えればプログラムの効率化に抜群の効果がありますし、他のプログラミング言語でも大体同じように使えるので一石二鳥です。

それに、containsindexOfは決まった文字列しか探せません。例えば「“abc”の後ろに数字09のどれかがある」という確認を一度にできません。検索したい文字列で何度もメソッド呼び出しを繰り返すしかなく、プログラムが長くなりがちです。でも、正規表現ならあっさりとできます。

要注意なのは、「含んでいるか」とするなら、正規表現の最初と最後に“.*”を付ける必要があることです。String.matchesは文字列全体とマッチングするので、「文字列全体のどこかにある」という意味にしなければならないからです。

これだと少し不便なので、シンプルに「含んでいる」を確認したいなら、Javaの正規表現に関するクラス(PatternMatcher)の使い方を覚える必要があります。それについては後述します。


3.配列が含んでいるか?

3-1.for文で探す

最も基本的なやり方で、配列全体をループして値を探します。比較する条件は自分で書くので、等しい、以上、以下、業務ロジックに則った値、いろいろできます。

とてもお手軽ですが、配列の要素が多くなればなるほど、確認に時間がかかります。場合によっては、後ろから探したり、途中から探したりするなどの工夫が必要になるかもしれません。

3-2.Arrays.asListでListに変換して探す

もっとシンプルに、配列が特定の値やインスタンスと等しいものを持つか確認するだけでいいなら、List.containsを使ってもいいでしょう。

List.containsを使うと、List中に「含まれているか」の確認ができます。ですので、Arrays.asListで配列からListを作れば、含まれているかの確認が簡単にできます。

なお、List.containsは、List上の各要素へequalsを呼び出して、探しているものと等しいかどうかを判断します。これには注意が必要です。

つまり、自分で作ったクラスの場合は、equalsをオーバーライドしていないと、List.containsでの判断が出来ないということです。標準APIにあるクラス(StringIntegerなど)はきちんとequalsが作られているので問題ありません。


4.Collection(List/Set/Queue/Stack)が含んでいるか?

4-1.Collection.containsで探す

List/Set/Queue/StackなどのCollectionにはcontainsというメソッドがあり、指定されたインスタンスを含んでいるかが分かります。

では、List/Set/Queue/Stackのcontainsを使ってみましょう。

4-1-1.大事なのはequalsがちゃんと作られているか

Collection.containsで重要なのは、Collectionに入れてあるクラスでequalsがちゃんと作られているかです。

Collectionの中に持っているインスタンスと、containsで指定されたインスタンスが同じかはObject.equalsの結果で判定されます。ですので、equalsがしっかりとオーバーライドされていなければcontainsは動きません。

Javaの標準APIのクラスなら大抵は大丈夫ですが、自作のクラスを使う場合は、きちんとequalsをオーバーライドしているか確認しましょう。

4-2.Collection.containsAllで探す

Collectionが、調べたいすべてのインスタンスを含むか確認したいなら、一度に調べるためのメソッドcontainsAllが使えます。containsAllを使えば、プログラムがより分かりやすくなります。

全てのインスタンスが含むか、ループをしてcontainsで調べてもいいのですが、そのようなプログラムを自分で書く必要はないのです。

調べたいインスタンスを持っているCollection(ListなどでOKです)を作り、containsAllを呼び出しましょう。


5.Mapが含んでいるか?

Mapは、キーと値をペアにして持つもので、プログラムでは大変良く使います。

Mapが何かを含むか確認したい時は、キーが含むか、値が含むかの二つのパターンがあります。

なお、Mapを使う上での前提条件ではありますが、キーとしては最低限Object.equalsをきちんと実装しているクラスでなければなりません。ものによっては、Object.hashCodeもきちんと実装している必要があります(HashMapを使う場合など)

5-1.Map.containsKeyでキーを探す

Map.containsKeyで、Mapのキーにその値を含んでいるかを確認できます。

5-2.Map.containsValueで値を探す

Map.containsKeyで、Mapの値がその値を含んでいるかを確認できます。

5-3.Mapのキーか値に複数の値を含んでいるか一度に探す

Mapのキーか値に複数の値が含まれるか一度に探したい場合、ズバリそのものなメソッドはないので、SetListに変換してからcontainsAllをするのが速いでしょう。

MapのキーをSetにするにはMap.keySetを、値をCollectionにするにはMap.valuesを使います。それぞれの結果に対して、containsAllをしてあげれば良いのです。


6.【応用】便利・高速な検索の仕方

6-1.文字列を正規表現で探す

java.util.regex.Matcherのfindを使うと、正規表現にマッチする文字列を「含んでいるか」を確認できます。Matcherは文字列が正規表現にマッチするか調べるクラスです。

Matcherを使うには、java.util.regex.Patternと組み合わせ、以下のようにします。Patternは、どういう正規表現かを表すクラスです。

  1. java.util.PatternのインスタンスをPattern.compileで作る
  2. 検索元の文字列をPattern.matcherで指定して、Matcherのインスタンスを作る
  3. 作ったMatcherfindmatchesを実行する

少々手順は多いですが、その分Javaでの正規表現のパワーをフルに使えます。それに、Patternは作ったものの使いまわしが出来ますので、調べなければならない文字列がたくさんある場合に便利です。

Javaでの正規表現の使い方や活用方法は、java.util.PatternJavadocや、以下の記事も参考にしてみてください。

関連記事

6-2.配列・CollectionからStreamで探す

Streamの終端操作anyMatchを使うと、Streamが条件を満たすものを含むか確認できます。Streamの中に、引数のPredicate.testtrueを戻すものが1つでもある場合はtrueが戻り、そうでなければfalseです。

下記はシンプルな例なので、Collection.containよりも面倒に思えかもしれません。ですが、複雑な条件での抽出をする場合は、こちらはループの処理がいらないので比較する処理に集中できますね。

条件を満たすものが一つもないか確認したい場合は、noneMatchを使います。

6-3.Arrays.binarySearchで高速に探す

とても大きなサイズの配列の中から探さなければならないなら、java.util.ArraysbinarySearchを活用できる場合があります。二分探索(バイナリサーチ)のアルゴリズムにより、高速に検索できます。

ただし、以下のArrays.binarySearchを使うには条件があります。

  • 配列の要素があらかじめソートされていなければならない
  • (Object版のみ)Comparableなクラスの配列でなければならない

公式Javadocの日本語訳は少々分かりづらいのですが、私たちの使い方なら、戻り値が0以上ならその値が配列中にある、と分かっていれば十分です。

なお、Arrays.binarySearchには、Comparatorを使うものもあります。こちらなら、Comparableなクラスではない配列も扱えます。この場合でも、binarySearchを呼び出す前にソートが必要なのは変わりません。

Arrays.binarySearchにはintなどのプリミティブ型版もあります。例えば、intの配列では以下のように使います。

ちなみに、Arrays.binarySearchは配列内の検索開始位置・終了位置を指定できるものもあります。必要ならこれらも使いましょう。

6-4.Collections.binarySearchで高速に探す

配列と同じように、値をたくさん持っているListcontainsで調べるのは時間がかかります。containsは律儀に先頭から全て調べるからで、もし後ろの方にあったなら、それだけ時間が無駄になります。

配列にはそのためのArrays.binarySearchがあるように、ListにもCollections.binarySearchがあります。

Collections.binarySearchを使う時のルールも、Arrays.binarySearchと同じです。

  • Listの要素があらかじめソートされていなければならない
  • Comparableなクラスを格納するListでなければならない

Collections.binarySearchには、Comparatorを使うものも用意されています。こちらの場合は、Listに格納されているクラスはComparableでなくても大丈夫です。


7.まとめ

この記事では、String、配列、Collection(List/Set)Mapについて、「含むかどうか」を確認する方法をお伝えしてきました。

基本的には、containsという名前かそれに近い名前のメソッドがあって、それぞれのクラスでの確認処理が行われるケースが多いです。

なお、この記事のサンプルでは、Javaの標準APIだけを用いました。Apache Commons CollectionsLangGoogleGuavaなどの有名なライブラリでは、もっといろいろなことが簡単にできますので、興味があればチャレンジしてみましょう!

私たちは、全てのエンジニアに市場価値を高め自身の望む理想のキャリアを歩んでいただきたいと考えています。もし、今あなたが転職を検討しているのであればこちらの記事をご一読ください。理想のキャリアを実現するためのヒントが見つかるはずです。

『技術力』と『人間力』を高め市場価値の高いエンジニアを目指しませんか?

私たちは「技術力」だけでなく「人間力」の向上をもって遙かに高い水準の成果を出し、関わる全ての人々に感動を与え続ける集団でありたいと考えています。

高い水準で仕事を進めていただくためにも、弊社では次のような環境を用意しています。

  • 定年までIT業界で働くためのスキル(技術力、人間力)が身につく支援
  • 「給与が上がらない」を解消する6ヶ月に1度の明確な人事評価制度
  • 平均残業時間17時間!毎週の稼動確認を徹底しているから実現できる働きやすい環境

現在、株式会社ボールドでは「キャリア採用」のエントリーを受付中です。

まずは以下のボタンより弊社の紹介をご覧いただき、あなたの望むキャリアビジョンをエントリーフォームより詳しくお聞かせください。

コメント