倭マン's BLOG

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

GDK のList がこんなに可愛いわけがない。 (List 編2) 要素の追加・削除

f:id:waman:20130628013258p:plain

今回は GDK が List インターフェースに追加しているメソッドのうち、要素の追加・削除に関連するメソッド。 前回見た要素の取得に関するメソッド getAt() と対になっている putAt() メソッドなどを見ていきます。 今回見ていくメソッド:

boolean addAll(int index, Object[] items)

void putAt(int idx, Object value)
void putAt(EmptyRange range, Object value)
void putAt(EmptyRange range, Collection value)
void putAt(IntRange range, Object value)
void putAt(IntRange range, Collection col)
void putAt(List splice, Object value)
void putAt(List splice, List values)

List plus(int index, Object[] items)
List plus(int index, List additions)
List plus(int index, Iterable additions)

List minus(Object removeMe)
List minus(Collection removeMe)
List minus(Iterable removeMe)
  • putAt() のインデックスを指定する範囲やコレクションが getAt() のものと違うのが悩ましい・・・ なんでこんなことになってんるんだ?
  • plus() メソッドは、引数が1つなら + 演算子として使えますが、ここで定義されているものは引数を2つ(以上)とるのでメソッド呼び出しとしてしか使えません

要素の追加に関しては挿入なのか置き換え(書き換え)なのかを注意する必要がありますね。 それと元のリストを変更するのか追加・削除したリストを返すのか(不変・可変)も重要ですね。 後でサンプルを見ていきます。

メソッド 返り値 since 説明
addAll(int, Object[]) boolean 1.7.2 指定された位置に要素を挿入する。 可変長引数として使える
putAt(int, Object)
putAt(EmptyRange, Object)
putAt(EmptyRange, Collection)
putAt(IntRange, Object)
putAt(IntRange, Collection)
putAt(List, Object)
putAt(List, List)
void 1.0
1.0
1.0
1.0
1.5.0
1.0
1.0
指定された位置の要素を置き換える。 空範囲の場合は挿入する
plus(int, Object[])
plus(int, List)
plus(index, Iterable)
List 1.8.1
1.8.1
1.8.7
元のリストは変更せずに、要素を挿入したリストを新たに作成して返す
minus(Object)
minus(Collection)
minus(Iterable)
List 1.0
1.0
1.8.7
元のリストは変更せずに、要素を削除したリストを新たに作成して返す

plus() や minus() はコレクションで演算子として使った場合に、元が不変で変更されたコレクションを返してたのと同様に、リストの場合も変更されされたリストを返します。 ただし、今の場合は plus() を演算子として使えませんが。

addAll() メソッド

addAll() メソッドはインデックスで指定した位置に引数の配列の要素を挿入します。 Groovy では引数最後の配列は可変長引数として扱われるので、addAll() の最後には複数の要素を書くことが出来ます:

def langs = ['Java', 'Java']
langs.addAll(1, 'Groovy', 'Scala')    // 可変長
assert langs == ['Java', 'Groovy', 'Scala', 'Java']

Java 標準 API の addAll(int, Collection) と挙動は同じですね。 使いやすさは向上しております。

putAt() メソッド

putAt() は結構たくさんシグニチャがありますが、

  • インデックスが
    • int
    • EmptyRange
    • IntRange
    • List
  • 置き換えるものが
    • Object
    • Collection, List

という風に分類できます(定義されていない組合せもありますが)。 ちょっと getAt() の場合とインデックスの型が異なりますが、これは別記事で比べたいと思います。 今回は仕様通りに。 putAt() は全て元のリストを変更します。 まずは int 値のみで位置を指定する場合:

// putAt(int, Object)
def langs0 = ['Java', 'Java']
langs0[1] = 'Groovy'
assert langs0 == ['Java', 'Groovy']

add() や addAll() と異なり値を置き換えます。 まぁ、標準 API の set() ですね。 この場合は置き換えるものとしてコレクションは指定できません。 次は EmptyRange(空の範囲)を指定した場合:

// putAt(EmptyRange, Object)
def langs1 = ['Java', 'Java']
langs1[0..<0] = 'Groovy'
assert langs1 == ['Groovy', 'Java', 'Java']

def langs2 = ['Java', 'Java']
langs2[2..<2] = 'Groovy'
assert langs2 == ['Java', 'Java', 'Groovy']

// putAt(EmptyRange, Collection)
def langs3 = ['Java', 'Java']
langs3[0..<0] = ['Groovy', 'Scala']
assert langs3 == ['Groovy', 'Scala', 'Java', 'Java']

この場合は範囲のスタート位置にオブジェクト(もしくはコレクション)が挿入されます。 0..<0 と 2..<2 は同じから要素でも挙動が(挿入される位置が)異なります。 こんな挙動でいいんだ・・・ なんかあんまり使わない方が良さそうな感じのメソッドだにゃあ。 次は IntRange が指定された場合:

// putAt(IntRange, Object)
def langs4 = ['Java', 'Java', 'Java']
langs4[1..2] = 'Groovy'
assert langs4 == ['Java', 'Groovy']

// putAt(IntRange, Collection)
def langs5 = ['Java', 'Java', 'Java']
langs5[1..2] = ['Groovy', 'Scala', 'Clojure']
assert langs5 == ['Java', 'Groovy', 'Scala', 'Clojure']

指定された範囲の要素はすべて削除され、代わりに指定したオブジェクトもしくはコレクションで置き換えられます。 指定した範囲とコレクションのサイズが違っても大丈夫です(リストのサイズも変わりますが)。 最後は引数に(Integer の)リストを指定した場合:

// putAt(List, Object)
def langs6 = ['Java', 'Java', 'Java', 'Java']
langs6[1, 3] = 'Groovy'
assert langs6 == ['Java', 'Groovy', 'Java', 'Groovy']

// putAt(List, List)
def langs7 = ['Java', 'Java', 'Java', 'Java']
langs7[1, 3] = ['Groovy', 'Scala']
assert langs7 == ['Java', 'Groovy', 'Java', 'Scala']

def langs8 = ['Java', 'Java', 'Java', 'Java']
try{
    langs8[1, 3] = ['Groovy', 'Scala', 'Clojue']
        // 指定したインデックスの個数とリストの個数は一致しているベシ
    assert false
}catch(IllegalArgumentException e){
    assert true
}

第2引数がオブジェクトの場合は指定したインデックスの位置を全てそのオブジェクトで置き換えます。 一方、第2引数がリストの場合はそれぞれ対応したインデックスの位置を指定したリストの要素で置き換えるので、このインデックス(のリスト)のサイズと指定したリストのサイズは一致している必要があります。 もし異なっていれば IllegalArgumentException が投げられます。

plus() メソッド

plus() メソッドは要素を挿入したリストを新たに生成して返します。 元のリストは変更されません。 通常、Groovy では plus() メソッドは+演算子を使用するために定義しますが、今の場合引数が2つ(以上)あるので+演算子としては使えません。 まぁ、使い方は簡単:

// plus(int, Object[])
def langs9 = ['Java', 'Java']
def result9 = langs9.plus(1, 'Groovy', 'Scala')    // 可変長
assert langs9 == ['Java', 'Java']
assert result9 == ['Java', 'Groovy', 'Scala', 'Java']

// plus(int, List)
def langs10 = ['Java', 'Java']
def result10 = langs10.plus(1, ['Groovy', 'Scala'])
assert langs10 == ['Java', 'Java']
assert result10 == ['Java', 'Groovy', 'Scala', 'Java']

minus() メソッド・- 演算子

minus() メソッドは remove(), removeAll() と同じように指定した要素を削除しますが、元のリストは変更せずに、要素を削除したリストを返します。 minus() は上記の plus() と違って-演算子で書けます。

// minus(Object)
def langs11 = ['Java', 'Groovy', 'Scala', 'Clojure']
def result11 = langs11 - 'Java'    // minus() メソッド
assert langs11 == ['Java', 'Groovy', 'Scala', 'Clojure']
assert result11 == ['Groovy', 'Scala', 'Clojure']

// minus(Collection)
def langs12 = ['Java', 'Groovy', 'Scala', 'Clojure']
def result12 = langs12 - ['Groovy', 'Scala']
assert langs12 == ['Java', 'Groovy', 'Scala', 'Clojure']
assert result12 == ['Java', 'Clojure']

def langs13 = ['Java', 'Groovy', 'Scala', 'Clojure']
try{
    def result13 = langs13.minus('Groovy', 'Scala')
        minsu(Object[]) はないので可変長引数としては書けない
    assert false
}catch(MissingMethodException e){
    assert true
}

minus(Object) というシグニチャは定義されていないので、addAll(Object) のように可変長引数のようには書けません。

次回はシーケンスというか、順序に関連したメソッドを見ていく予定。 ただ、getAt(), putAt() のインデックスに関する番外編を書くかも。

プログラミングGROOVY

プログラミングGROOVY