No Programming, No Life

プログラミング関連の話題や雑記

GroovyなJDK、それがGDK(java.util.List編)


このシリーズの一覧はこちら

はじめに

GroovyにはJavaの標準API(JDK)を拡張したGroovy JDK(GDK)があります。大量の便利メソッドが追加されており、これを使いこなすだけでも相当のことができるようになります。このシリーズでは毎回1クラスずつ各メソッドの使用例をサンプル付きでご紹介していきたいと思います。
「今回は java.util.List です」

ListはMap、Closureと並んで、もはやGroovyにとっては無くてはならない存在と言っても良いでしょう。
ソースコードの至る所で利用されており、シンタクスシュガーとして [] という初期化記法も用意されいます。
Listには数多くのリスト処理用のメソッドが追加されており、非常に便利に利用することが出来るのが特徴の一つです。
中には#takeや#drop、#headや#tailなど、関数型言語のリスト処理でおなじみのメソッドも用意されていますので
関数型言語に親しみの深い方でもすぐに利用していただけるでしょう。

イテレート系メソッド

Object first() [since v1.5.5]

リストの先頭要素を取得します。いわゆるcarです。空リストの場合、エラーとなります。
headfirstは同じ動きになります。

assert [1, 2, 3].first() == 1
assert [1, 2].first() == 1
assert [1].first() == 1
def shouldFail = {
  try {
    [].first() // 空リストのfirst()を取得しようとするとエラー!
  } catch (NoSuchElementException e) {
    assert e
    return
  }
  assert false
}
shouldFail()

Object last() [since v1.5.5]

リストの最終要素を取得します。空リストの場合、エラーとなります。

assert [1, 2, 3].last() == 3
assert [1, 2].last() == 2
assert [1].last() == 1
def shouldFail = {
  try {
    [].last() // 空リストのlast()を取得しようとするとエラー!
  } catch (NoSuchElementException e) {
    assert e
    return
  }
  assert false
}
shouldFail()

Object head() [since v1.5.5]

リストの先頭要素を取得します。いわゆるcarです。空リストの場合、エラーとなります。
headfirstは同じ動きになります。

assert [1, 2, 3].head() == 1
assert [1, 2].head() == 1
assert [1].head() == 1
def shouldFail = {
  try {
    [].head() // 空リストのhead()を取得しようとするとエラー!
  } catch (NoSuchElementException e) {
    assert e
    return
  }
  assert false
}
shouldFail()

List tail() [since v1.5.6]

リストの先頭要素を除いた後続の要素をリストで取得します。いわゆるcdrです。空リストの場合、エラーとなります。

assert [1, 2, 3].tail() == [2, 3]
assert [1, 2].tail() == [2]
assert [1].tail() == []
def shouldFail = {
  try {
    [].tail() // 空リストのtail()を取得しようとするとエラー!
  } catch (NoSuchElementException e) {
    assert e
    return
  }
  assert false
}
shouldFail()

List take(int num) [since v1.8.1]

リストの先頭要素から指定した数分の要素を取得します。
指定数が0の場合は空リスト、指定数が要素数以上の場合は、全要素を取得します。
いずれの場合も新しいリストを作成され返却されるため、返却されたリストに変更を加えても元のリストには影響が及びません。
動き的にはdropの逆と考えることもできるでしょう。

def list = [1, 2, 3, 4, 5, 6]
assert list.take(0) == []     // 先頭から0個要素を取得(空リスト)
assert list.take(1) == [1]    // 先頭から1個要素を取得
assert list.take(2) == [1, 2] // 先頭から2個要素を取得
assert list.take(7) == [1, 2, 3, 4, 5, 6] // 取得する多い場合、全リスト(新しいリスト)を返却

List drop(int num) [since v1.8.1]

リストの先頭要素から指定した数分の要素を取得します。
指定数が0の場合は空リスト、指定数が要素数以上の場合は、全要素を取得します。
いずれの場合も新しいリストを作成され返却されるため、返却されたリストに変更を加えても元のリストには影響が及びません。
動き的にはtakeの逆と考えることもできるでしょう。

def list = [1, 2, 3, 4, 5, 6]
assert list.drop(0) == [1, 2, 3, 4, 5, 6] // 先頭から0個要素が除去された新しいリストを返却
assert list.drop(1) == [2, 3, 4, 5, 6]    // 先頭から1個要素が除去された新しいリストを返却
assert list.drop(2) == [3, 4, 5, 6]       // 先頭から2個要素が除去された新しいリストを返却
assert list.drop(7) == [] // 削除する要素の方が多い場合、空リストを返却
assert list == [1, 2, 3, 4, 5, 6]

boolean push(Object value) [since v1.5.5]

リストに要素を追加します。addの別名です。
pushpopを使ってStackを真似ることができます。

def list = []
assert list.push(1) // 追加が成功するとtrueが返る
assert list.push(2)
assert list.push('x')
assert list == [1, 2, 'x']

Object pop() [since v1.0]

リストの最後から要素を取り出します。取り出す要素がない(空リスト)の場合エラーが発生します。
pushpopを使ってStackを真似ることができます。

def list = [1, 2, 'x']
assert list.pop() == 'x' // 取り出した要素が戻り値となる
assert list == [1, 2]
assert list.pop() == 2
assert list.pop() == 1
assert list == [] // この時点では空となる
def shouldFail = {
  try {
    assert list.pop() // 空をpopしようとするとエラー発生
  } catch (NoSuchElementException e) {
    assert e
    return
  }
  assert false
}
shouldFail()

boolean addAll(int index, Object[] items) [since v1.7.2]

リストにオブジェクト配列の要素をすべて挿入するメソッドです。addAllした後の戻り値は追加できたかどうかがbooleanで返却されます。*1

def list = []
def today = new Date()
def adds = [1, 'a', today] as Object[]
assert adds.class.name == '[Ljava.lang.Object;'
assert list.addAll(adds)
assert list == [1, 'a', today]

leftShift(<<)との違いは、例えばこの例だと、addAllの代わりに、leftShiftにすると以下のようになります。

list << [1, 'a', today]
assert list == [[1, 'a', today]]

List asImmutable() [since v1.0]

Comming Soon...

List asSynchronized() [since v1.0]

Comming Soon...

List transpose() [since v1.5.0]

Comming Soon...

Set permutations() [since v1.7.0]

Comming Soon...

Set subsequences() [since v1.7.0]

Comming Soon...

List reverse() [since v1.0]

リストの要素を反転させます。こちらの引数なしのバージョンは元の要素には影響が及ばないようになっています。
引数ありバージョンの方では変更が元の要素にも影響を及ぼします。

def list1 = [1, 2, 3]
def list2 = list1.reverse() // 要素が反転したリストを取得
assert list2 == [3, 2, 1]
list2[1] = 10
assert list2 == [3, 10, 1]
assert list1 == [1, 2, 3] // 元の要素には変更が影響しない

List reverse(boolean mutate) [since v1.8.1]

リストの要素を反転させます。こちらの引数ありのバージョンは元の要素に影響を及ぼします。
引数なしバージョンの方では変更が元の要素にも影響を及びません。

def list1 = [1, 2, 3]
assert list1.reverse(true) == [3, 2, 1] // リストを反転させる
assert list1 == [3, 2, 1] // list1が直接反転させられている

SpreadMap toSpreadMap() [since v1.8.0]

リストの並びからキー=値、のマップを作成します。要素は偶数個である必要があり、奇数個だとエラーとなります。

// 1, 3, 5 をキーとし、2, 4, 6 を値とするマップを作成
def map = [1, 2, 3, 4, 5, 6].toSpreadMap()
assert map[1] == 2
assert map[3] == 4
assert map[5] == 6

def failTest = {
  try {
    def map2 = [1, 2, 3].toSpreadMap() // 要素は偶数個である必要があります
  } catch (groovy.lang.GroovyRuntimeException e) {
    assert e
    return
  }
  assert false
}
failTest()

def map3 = [1, 3, 1, 4].toSpreadMap()
assert map3[1] == 4 // 後から同じキーが指定されると上書きされます

def map4 = ['abc', 1, 'def', 'ghi'].toSpreadMap()
assert map4.abc == 1     // キーに文字列の指定も勿論可能です
assert map4.def == 'ghi'

SpreadMapについては、SpreadMap#SpreadMapおよびMap#toSpreadMapもご参照下さい。

List minus(Collection removeMe) [since v1.0]

Comming Soon...

List minus(Object operand) [since v1.0]

Comming Soon...

List plus(int index, Object[] items) [since v1.8.1]

Comming Soon...

List plus(int index, List additions) [since v1.8.1]

Comming Soon...

List getAt(Range range) [since v1.0]

Comming Soon...

List getAt(EmptyRange range) [since v1.0]

Comming Soon...

List getAt(Collection indices) [since v1.0]

Comming Soon...

Object getAt(int idx) [since v1.0]

Comming Soon...

void putAt(int idx, Object value) [since v1.0]

Comming Soon...

void putAt(EmptyRange range, Object value) [since v1.0]

Comming Soon...

void putAt(EmptyRange range, Collection value) [since v1.0]

Comming Soon...

void putAt(IntRange range, Collection col) [since v1.5.0]

Comming Soon...

void putAt(IntRange range, Object value) [since v1.0]

Comming Soon...

void putAt(List splice, List values) [since v1.0]

Comming Soon...

void putAt(List splice, Object value) [since v1.0]

Comming Soon...

List reverseEach(Closure closure) [since v1.5.0]

Comming Soon...

boolean equals(Object[] right) [since v1.5.0]

Comming Soon...

boolean equals(List right) [since v1.0]

Comming Soon...

Process execute() [since v1.0]

Comming Soon...

Process execute(String[] envp, File dir) [since v1.7.1]

Comming Soon...

Process execute(List envp, File dir) [since v1.7.1]

Comming Soon...

*1:追加失敗することってあまりない気がしますけども。