ScalaとJavaの配列とかコレクションとか

Scalaだけ使ってると何気なくSeqやList使ってるんだけど
Javaのライブラリと併せて使おうとすると、Javaのコレクションと
名前が若干変わっていて、結構混乱することがあったのでまとめ。

わかりやすくまとめてくれている記事は他にもたくさんあるので、
もし参考にしたい方はそちらを参照したほうがよいかもしれません。

参考にさせていただいたリンク

最初に、当記事は、こちらの内容から僕が欲していたものを
かいつまんだだけなので、先に参考リンクを貼っておきます。

大前提として

今回扱うテーマ

ブログタイトルはざっくりだけど、ScalaとJavaそれぞれのCollectionの相互利用と変換
一応僕がやってるのがScalaなんで、Scalaのほうに重点あたってると思います。

ScalaとJavaのCollectionの一番大きな違い

Scalaにはimmutableとmutableがあり、Javaはimmutableとmutableを区別しない(全部mutable, final修飾子を
つけることによって再代入できなくすることはできるが、Scalaの場合はそれがクラスで定義される)

どんなクラス(トレイト・インタフェース)があるのか

もちろん全て列挙することは難しいので、代表的なものでScalaとJavaで
対応するものを挙げていきます。

単純な配列

Java

どっちでもOK

Scala

ScalaではJavaの配列はArrayクラスで実装されている。
が、Javaとくらべてジェネリクス型になっており、Javaのプリミティブ型以外の
クラスでも利用することができます。

上記のJavaコードと互換があるインスタンスを生成したい場合はこんな感じ。

注意したいのはJavaのString[]とScalaのArray[String]が内部的に同じクラス
になっている、というわけではないことに注意。
implicit conversionが利用されているんですが、それに関しては後述します。

Javaの[List, Set, Map]とScalaの[Seq, Set, Map]

そもそもList,Set,Mapってなに、という場合

  • List
    配列はランダムアクセスすることに対して
    シーケンシャルアクセスを行って対象にアクセスするデータ構造。
    検索、値の変更は遅いけど、挿入・削除は早い
  • Set
    重複しない同じクラスのインスタンスの集合を扱う
  • Map
    キーと値をセットにしてデータを管理する。キーは重複しない。

そして最初にも書いたけど、Javaのコレクションに共通するのがmutableであること。
インスタンスの初期化時以外にも値が変更可能です。

この3種類のデータ構造がJavaでよく用いられるのに対して、Scalaで用意されて
いるのがSeq,Set,Mapで、全てimmutableで、かつ関数型言語っぽいメソッドが追加されている感じです。

それぞれの言語でデータを扱うための超基本的なクラスではありますが
各言語の3つのクラスは、それぞれ互換性はありません。
ScalaからJavaの3つのクラスのインスタンスを扱うためには別のクラスが必要になります。
(一番はやはりmutableかimmutableかの違いがあるため)

互換性を保つために用意されたクラス群はscala.collection.mutableパッケージに含まれています。
こちらは明示的にimportをしなくては利用できませんが、上記にあげたScalaの3つのクラスは
scala.collection.immutableに含まれていて、importしなくても利用することができます。

それじゃScalaのListってなんなんだろう

こちら、僕もちゃんと調べられていないので、まとめて別記事で。
すいません。とりあえず、java.util.Listとは別物です。

JavaとScalaのコレクション、文字列、配列の互換性はどのように実現されているのか

これらの互換性はクラスそのもので実現しているのではなく、implicit conversionで実現されています。

配列 < -> scala.collection.mutable.WrappedArrayクラス
文字列 < -> scala.collection.immutable.WrappedStringクラス

でそれぞれ必要に応じて自動的に変換されます。

コレクションに関しては

Java と Scala 間のコレクションの変換
で一覧になっています。これはimport collection.JavaConversions._をimportすることで
利用可能になります。しつこいようですが、implicit conversionで取得したインスタンスは
mutableパッケージ内のクラスのインスタンスに変換されていることに注意です。

Traversableトレイト

Scalaの全てのコレクションが実装しているトレイト。

ここで定義されているメソッドは、全てのコレクションに共通する機能となるので
ざっくりと紹介しておきます。

  • ++

  • map (コレクションに関数fを適用して、新しいコレクションを返す)

  • flatMap (コレクションに関数fを適用して、新しいコレクションを連結して返す)

  • collect (パターンマッチングにマッチした場合のみ、関数を適用して新しいコレクションを返す)

  • toArray,toList,toIterable,toSeq,toIndexedSeq,ToStream,toSet,toMap
    名前の通り、別のコレクションに変換したいとき
  • isEmpty,nonEmpty,size, hasDefiniteSize
    コレクションが空のとき、そうでないとき、要素数、そして、有限サイズであるかをそれぞれ返す
  • head
    コレクションの先頭を返す

  • headOption

  • last,lastOption
    コレクションの最後尾を返す。lastOptionはコレクションに値がない場合にNoneを返す
  • find
    コレクションの中から条件にあう最初の要素を返す。なければNone

  • tail
    コレクションの先頭以外を返す
  • init
    コレクションの最後尾以外を返す
  • slice
    インデックスがfromからtoまでのコレクションを返す

  • take
    先頭からn個の要素を返す

  • drop
    先頭からn個を除いた要素を返す

  • takeWhile,dropWhile
    条件を満たしている間、条件を満たした要素を返す
    条件を満たさないとき処理を終了する(dropWhileはその逆)

  • filter,filterNot,withFilter
    条件にあう要素だけをリストにして返す(filterNotは逆)
    withFilterはfilterより高速だが、後続に使えるメソッドが限られている。
    今回は扱わない。
  • spliteAt
    指定した位置でコレクションを分割

  • span
    条件を満たす先頭部分と、満たさない残りを返す ( xs.takeWhile(f), xs.dropWhile(f))

  • partition
    条件を満たす部分と満たさない残りを返す ( xs.filter(f), xs.filterNot(f) )

  • groupBy
    条件によって自動でキーを生成して、マッチングしたグループに割り当てる

  • foldLeft(/:と同じ)
    左畳込み。初期値と、関数を渡して、先頭から順番に演算する

  • foldRight(:と同じ)
    右畳込み。初期値と、関数を渡して、末尾から順番に演算する

foldLeftは初期値が左、foldRightは初期値が右とおぼえておいたほうがいいかも。
※上記の例だと1回目に0がzに束縛される。
* reduceLeft,reduceRight
foldLeftとfoldRightの結果をコレクションにして返す
* sum, product
全ての要素を足す、掛ける
* min,max
最小・最大を返す
* mkstring
連結した文字列を返す。先頭、区切り、最後尾の文字を指定できる

  • addString
    mkStringと同じ使い方でStringBuilderを返す
  • stringPrefix
    クラス名の文字列表現を返す
  • collectFirst
    collectで取得できるリストの先頭を返す
  • flatten
    コレクションの要素がTraversableの場合、それを平坦化する

  • unzip, unzip3
    flattenの逆。タプル(unzip)かトリプル(unzip3)が要素だった場合に
    それぞれを分離して新しいコレクションを返す

多!!!これでも全部じゃないですただ、よく使いそうな
コレクションのメソッドはだいたいTraversableに含まれているんですね。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です