倭マン's BLOG

くだらない日々の日記書いてます。 たまにプログラミング関連の記事書いてます。 書いてます。

GDK のコレクションがこんなに便利なわけがない。 (Collection 編2) 要素の追加・削除

今回は GDK が Collection クラスに追加している要素の追加・削除に関連するメソッド。 要素の追加・削除は Java の標準 API にもあれこれありますが、GDK が追加しているのは Groovy の演算子に対応するもの(plus(), leftShift())、可変長引数(配列 Object[] を引数にとる)、クロージャを引数にとるもの、などです:

// 要素の追加
Collection plus(Object right)
Collection plus(Collection right)
Collection plus(Iterable right)

Collection leftShift(Object value)

boolean addAll(Object[] items)

// 要素の削除
boolean removeAll(Object[] items)
boolean removeAll(Closure condition)

boolean retainAll(Object[] items)
boolean retainAll(Closure condition)

各メソッドは Groovy でよくあるもの、もしくは Java 標準 API で同名のメソッドがあるものなので、使い方は難しくないと思いますが、それぞれのメソッドが元のコレクションを不変に保つのか要素を変更するのかは注意が必要かと。 返り値が Collection ではないもの(addAll(), removeAll(), retainAll())は元のコレクションを変更するのはいいかと思いますが*1、plus() は元のコレクションを変更しませんが、leftShift() は元のコレクションを変更します。 まぁ、以下のサンプルでそれぞれ試してるので、参考に。

要素の追加


まずは要素を追加するメソッド。 plus() は「+」演算子、leftShift() は「<<」演算子として使うのが普通かと。

メソッド 返り値 since 説明
plus(Object)
plus(Collection)
plus(Iterable)
Collection 1.5.0
1.8.7
1.5.0
要素の追加(+演算子
leftShift(Object) Collection 1.0 要素の追加(<< 演算子
addAll(Object[]) boolean 1.7.2 引数の要素を全て追加

Groovy では引数の最後が配列なら、可変長引数として使えるので、addAll(Object[]) は要素を列挙して追加するのに使えます。 ではサンプルコード:

def langs = ['Java']

// plus(Object), +
def langs1 = langs + 'Groovy'
assert langs == ['Java']    // 元のコレクションは変更されない
assert langs1 == ['Java', 'Groovy']

// plus(Collection), +
def langs2 = langs1 + ['Scala', 'Clojure']
assert langs1 == ['Java', 'Groovy']    // 元のコレクションは変更されない
assert langs2 == ['Java', 'Groovy', 'Scala', 'Clojure']

// leftShift(), <<
def langs3 = langs2 << 'Jython'
assert langs2 == ['Java', 'Groovy', 'Scala', 'Clojure', 'Jython']    // 元のコレクションも変更される
assert langs3 == ['Java', 'Groovy', 'Scala', 'Clojure', 'Jython']

langs3 << ['JRuby', 'Fantom']
assert langs3 == ['Java', 'Groovy', 'Scala', 'Clojure', 'Jython', ['JRuby', 'Fantom']]
    // コレクションを << で追加すると、1つのオブジェクトとして処理される

def langs4 = ['Java', 'Groovy'] << 'Scala' << 'Clojure'    // << 演算子を連結できる
assert langs4 == ['Java', 'Groovy', 'Scala', 'Clojure']

// addAll()
assert langs == ['Java']
langs.addAll('Groovy', 'Scala', 'Clojure')    // 可変長引数
assert langs == ['Java', 'Groovy', 'Scala', 'Clojure']
  • + 演算子は元のコレクションを変更しません
  • << 演算子は連結して使えます。 他の多くのクラスでこのように使用できるようにするため(StringBuilder や Writer なども含め)、<< 演算子は元のオブジェクトを返すようになっていると思われます
  • << 演算子はあくまで add() と同じで、addAll() の機能はありません。 つまり、コレクションを << 演算子で追加しても、1つのオブジェクトとして追加されます
  • addAll(Object[]) は可変長引数として扱えます

要素の削除


次は要素を削除するメソッド。 Java の標準 API に同名のメソッドがあるのであまり使い方は問題ないかと。 これらはどちらも元のコレクションを変更します。

メソッド 返り値 since 説明
removeAll(Object[])
removeAll(Closure)
boolean 1.7.2 引数の要素もしくは引数で指定される要素を全て削除
retainAll(Object[])
retainAll(Closure)
boolean 1.7.2 引数の要素もしくは引数で指定される要素以外を全て削除

addAll() と同じく、配列を引数にとるものは可変長引数として扱えるので、要素を列挙して使用できます。 クロージャを引数にとるものは、boolean 値を返すクロージャをとり、removeAll() は true を返す要素を削除し、retainAll() は true を返す要素を残します。 まずは removeAll() から

def langs = ['Java', 'Groovy', 'Scala', 'Clojure', 'Jython', 'JRuby', 'Fantom']

// removeAll(Object[])
langs.removeAll('Scala', 'Clojure', 'Fantom')
assert langs == ['Java', 'Groovy', 'Jython', 'JRuby']

// removeAll(Closure)
langs.removeAll{ it.startsWith 'J' }
assert langs == ['Groovy']

特に問題ないかと。 retainAll() は removeAll() と削除するか残すかが逆なだけですね。

langs = ['Java', 'Groovy', 'Scala', 'Clojure', 'Jython', 'JRuby', 'Fantom']

langs.retainAll('Java', 'Groovy', 'Scala', 'Clojure')
assert langs == ['Java', 'Groovy', 'Scala', 'Clojure']

langs.retainAll{ it.startsWith 'J' }
assert langs == ['Java']

Java 標準 API に同名のメソッドがあれば元のコレクションを変更するかどうかはそれに合わせてあるハズなので、その辺りから元のコレクションが変更されるかどうかを知っておくのがいいかと。

次回は要素を検索するメソッド(findXxxx() と言う名前のメソッド)の予定。

プログラミングGROOVY

プログラミングGROOVY

*1:Java 標準 API の同名のメソッドが元のコレクションの要素を変更するので、GDK が追加するメソッドも同じ挙動にしているんでしょう。