倭マン's BLOG

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

Groovy in Addiction (6) : リスト List

今回はリスト (java.util.List) について。 『Groovyイン・アクション』Chapter4 とGroovy JDKを参考にしています(一覧)。

Groovyイン・アクション

Groovyイン・アクション

インスタンス生成


List インスタンスの生成には

などがあります。 ここで見ていく List の実装クラスは

です。

ArrayList

扱う List の実装に特に関心がないなら ArrayList を使いましょう。 一応、コンストラクタから直接インスタンスの生成も出来ます。 「リスト展開演算子 *」も使えます(参考)。

def list1 = [1, 2, 3];
assert list1 instanceof ArrayList;

def list2 = [];
assert list2 instanceof ArrayList;

def list3 = new ArrayList();
assert list3 instanceof ArrayList;

def list4 = [1, *[2, 3]];
assert list4 == [1, 2, 3];

def list5 = [*list4, 4, 5];
assert list5 == [1, 2, 3, 4, 5];

LinkedList

LinkedList を指定して使用したいときは、コンストラクタを直接呼び出すか、LinkedList に変換する必要があります。

//***** LinkedList *****
def linkedList1 = new LinkedList();
assert linkedList1 instanceof LinkedList;

def linkedList2 = new LinkedList([1, 2, 3]);
assert linkedList2 instanceof LinkedList;

def linkedList3 = [1, 2, 3] as LinkedList;
assert linkedList3 instanceof LinkedList;

3つ目が個人的には好き。

List としての Range

Range は List のサブクラスなので、List 型に変換しなくても List として使用できますが、可読性のために toList() メソッドや asType によって、List として使用することを明示しておいたほうが良いかと。

//***** Range *****
def rangeList1 = (1..100);
assert rangeList1 instanceof List;

def rangeList2 = (1..100).toList();
assert rangeList2 instanceof ArrayList;

def rangeList3 = (1..100) as List;
assert rangeList3 instanceof List;

その他

toList() メソッドが定義されているクラスから List を生成するのも便利なことが多いかと。

def strList1 = 'ABCDE'.toList();
assert strList1 == ['A', 'B', 'C', 'D', 'E'];

def strList2 = 'ABCDE' as List;
assert strList2 == ['A', 'B', 'C', 'D', 'E'];

要素の取得と設定 : getAt & putAt


getAt(), putAt() メソッドは要素の取得設定を行います。 Java API の get(), set() メソッドの拡張です。 基本的にはリストのサイズを変更しません(サイズを超える添字に対する putAt() はサイズを変える)。

メソッド名 返り値 メソッド名
(Generics)
返り値
(Generics)
getAt(int) Object getAt(int) E
getAt(Range)
getAt(EmptyRange)
getAt(Collection)
List getAt(Range<int>)
getAt(EmptyRange<int>)
getAt(Collection<int>)
List<E>
putAt(int, Object) void putAt(int, E) void
putAt(IntRange, Object)
putAt(IntRange, Collection)
putAt(EmptyRange, Object)
putAt(EmptyRange, Collection)
putAt(List, Object)
putAt(List, List)
void putAt(IntRange, E)
putAt(IntRange, Collection<E>)
putAt(EmptyRange<int>, E)
putAt(EmptyRange<int>, Collection<E>)
putAt(List<int>, E)
putAt(List<int>, List<E>)
void

表の右2列の Generics は List<E> に対して勝手に書いてます*1。 Groovy に型指定なんてしても仕方ないかと思いますが。

以下、メソッドの使用例を見ていきます:

//***** getAt(int) *****
list = ['a', 'b', 'c', 'd', 'e', 'f'];

assert list[1] == 'b';
assert list[-1] == 'f';
assert list[6] == null;
  • 負の添字も使えます。 取得方法は、リストの末尾から逆に数えていきます。
  • サイズを超えた添字では null が返されます。
//***** get(IntRange) など *****
list = ['a', 'b', 'c', 'd', 'e', 'f'];

assert list[1..3] == ['b', 'c', 'd'];   //getAt(IntRange)
assert list[] == [];    // getAt(EmptyRange)

assert list[1, 3, 5] == list[[1, 3, 5]];    // getAt(Collection) その1
assert list[1, 3, 5] == ['b', 'd', 'f'];
assert list[1, 5, 3] == ['b', 'f', 'd'];

set = [0, 1] as HashSet;
resultList = list[set];    // getAt(Collection) その2
assert resultList == ['a', 'b'] || resultList == ['b', 'a'];
  • getAt(IntRange) などでは、逆順序の範囲や負の添字も使えますが、見た目にややこしいものは使わないのが無難。
  • getAt(Collection) は引数に List 以外のコレクションは使わない方が良いかと(順序が不明なのに返り値は List)。
//***** put(int, Object) *****
list = ['a', 'b', 'c'];
list[1] = 'x';
assert list == ['a', 'x', 'c'];

list = ['a', 'b', 'c'];
list[-1] = 'x';
assert list == ['a', 'b', 'x'];

list = ['a', 'b', 'c'];
list[4] = 'x';
assert list == ['a', 'b', 'c', null, 'x'];
  • getAt(int) と同じように負の添字も使えます。 設定される箇所も getAt(int) と同じ。
  • サイズを超えた添字では List のサイズが拡張されて設定されます。 途中は null で穴埋め。
//***** put(IntRange, Object) など *****
list = ['a', 'b', 'c'];
list[1..2] = 'x';    // putAt(IntRange, Object)
assert list == ['a', 'x'];    // list != ['a', 'x', 'x']

list = ['a', 'b', 'c'];
list[1..2] = ['x', 'y'];    // putAt(IntRange, Collection) その1
assert list == ['a', 'x', 'y'];

list = ['a', 'b', 'c'];
list[1..2] = ['x', 'y', 'z'];    // putAt(IntRange, Collection) その2
assert list == ['a', 'x', 'y', 'z'];

list = ['a', 'b', 'c'];
list[1..2] = [];    // putAt(IntRange, Collection) その3
assert list == ['a'];

list = ['a', 'b', 'c'];
list[] = 'x';    // putAt(EmptyRange, Object)
assert list == ['a', 'b', 'c'];

//list = ['a', 'b', 'c'];
//list[] = ['x', 'y'];    // putAt(EmptyRange, Collection)
//assert list == ['a', 'b', 'c'];

list = ['a', 'b', 'c'];
list[0, 2] = 'x';    // putAt(List, Object)
assert list == ['x', 'b', 'x'];

list = ['a', 'b', 'c'];
list[0, 2] = ['x', 'y'];    // putAt(List, List)
assert list == ['x', 'b', 'y'];
  • putAt(IntRange, Object) などでも、getAt(IntRange) などと同じように、ややこしい逆順序や負の添字は使わない方が無難。
  • putAt(EmptyRange, Collection) は例外が投げられます(Groovy 1.7 では)。
  • putAt(List, List) では、2つの引数の List のサイズが異なると例外が投げられます。

要素の追加と削除 : plus & minus


plus(), minus() メソッドは要素の追加削除を行います。 Java API の add(), addAll(), remove(), removeAll メソッドなどの拡張です。 基本的にリストのサイズを変更します(追加、削除が成功すれば)。

メソッド名 返り値 メソッド名
(Generics)
返り値
(Generics)
備考
plus(Object)
plus(Collection)
Collection plus(E)
plus(Collection<E>)
Collection<E> Collection に定義されてます
minus(Object)
minus(Collection)
List minus(E)
minus(Collection<E>)
List<E>
leftShift(Object) Collection leftShift(E) Collection<E> Collection に定義されてます

注意が必要なのは、要素が追加、削除されるのは plus(), minus() メソッド(+, - 演算子)の返り値のリストであることでしょうか。 元々のリストは変更されないので、変数の再代入(もしくは +=, -= 演算子)が必要です。

plus(), +

//***** plus() *****
list = [];

list += 'a';    // plus(Object)
assert list == ['a'];

list += ['b', 'c'];    // plus(Collection)
assert list == ['a', 'b', 'c'];

minus(), -

//***** minus() *****
list = ['a', 'b', 'c', 'd'];

list -= 'c';    // minus(Object)
assert list == ['a', 'b', 'd'];

list -= ['a', 'd'];    // minus(Collection)
assert list == ['b'];

leftShift, <<

//***** leftShift() *****
list = [];

list << 'a';    // leftShift(Object)
assert list == ['a'];

list << 'b' << 'c';
assert list == ['a', 'b', 'c'];

leftShift() は再代入をする必要はない模様。

リストと制御構造

これらに関連するメソッドは下表:

メソッド名 返り値 備考
asBoolean() boolean Collection に定義されてます
isCase(Object) boolean Collection に定義されてます

if 文

要素が空なら false、そうでないなら true

list = [1, 2, 3];
if(list)
    assert true;
else
    assert false;

list = [];
if(list)
    assert false;
else
    assert true;

switch-case 文

list = ['A', 'B', 'C', 'D'];
switch('C'){
    case list:
        assert true;
        break;
    default:
        assert false;
}

switch('c'){
    case list:
        assert false;
        break;
    default:
        assert true;
}

grep()

list = ('A'..'D') as List;
target = 'PASSWORD'.toList();    // target == ['P', 'A', 'S', 'S', 'W', 'O', 'R', 'D']
assert target.grep(list) == ['A', 'D'];

for-in 文

log = '';
for(i in [1, 'x', 5])
    log += i;

assert log == '1x5';

*1:仕様とかではなく、正確でもありません。 コンパイルも通りません