GS Collections 使い方メモ

[Java Magazine][3] で取り上げられていて存在は知っていたものの、特にチェックはしてなかった GS Collections。

[Java Day Tokyo 2015][1] や [JJUG CCC 2015][2] で再びその名前を耳にし、気になってチラッと実装を見たところ、面白そうだったので使い方を調べてみた。

# 【追記】Eclipse Collections
- 2015 年の 10 月から 12 月にかけて、 GS Collections は Eclipse 財団に移管された。
- 名前も GS Collections から Eclipse Collections に変更された。
- 公式サイトはこちら  
→ [Eclipse Collections - Features you want with the collections you need. (日本語ページ)](https://www.eclipse.org/collections/ja/)
- GS Collections と、機能的な差はない。
    - パッケージ名は変わってる。
- コントリビュータを広く募集するようになった（今までは issue の受付はやっていたけど、コントリビュータの受付はしていなかったぽい）。
- 2016 年中に ver 8 をリリースする予定で、 Java 8 より前の後方互換を撤廃する大きな変更があるらしい。
    - そのへんの話は、[JJUG CCC 2015 Fall の資料](http://www.java-users.jp/?page_id=2064#GH-4) に載ってる。

#GS Collections とは
- Goldman Sachs （ゴールドマン・サックス）という企業が内部で開発したコレクションフレームワーク。
- 2012 年にオープンソースとして公開された（[goldmansachs/gs-collections | GitHub](https://github.com/goldmansachs/gs-collections)）。
- JDK 標準のコレクション（`ArrayList` や `HashMap` など）と比べると、以下のような特徴がある。
    - 標準のコレクションフレームワークには存在しないコレクションが追加されている（`Bags`、`BiMap`、`ImmutableList` などなど）。
    - メモリ効率が最適化されている。
    - Smalltalk に存在した `select` や `reject`、 `collect` などの便利なメソッドが取り入れられ、 Stream API よりも簡潔にコレクションを操作できる。

#インストール
Eclipse Collections はこっち

```groovy:build.gradle
    compile 'org.eclipse.collections:eclipse-collections:7.0.0'
```

[Maven Repository: eclipse-collections](http://search.maven.org/#search%7Cgav%7C1%7Cg:%22org.eclipse.collections%22%20AND%20a:%22eclipse-collections%22)

```groovy:build.gradle
    compile 'com.goldmansachs:gs-collections:6.1.0'
```

[Maven Repository: com.goldmansachs](http://mvnrepository.com/artifact/com.goldmansachs)

#Hello World
```java:Main.java
import com.gs.collections.impl.factory.Lists;

public class Main {
    public static void main(String[] args) {
        Lists.immutable
             .of("one", "two", "three", "four")
             .select(s -> s.contains("o"))
             .collect(s -> "[" + s + "]")
             .each(System.out::println);
    }
}
```

```text:実行結果
[one]
[two]
[four]
```

#Mutable と Immutable
GS Collections のコレクションは、その性質から Mutable と Immutable の２つに大きく分けることができる。

Mutable はコレクションの要素を変更するための `add()` などの更新系のメソッドが定義されている。
しかし、Immutable なコレクションにはそれら更新系のメソッドが一切定義されていない。

例えば、 `List` の Mutable と Immutable は以下のようにインターフェースが定義されている。

![gscollections.jpg](https://qiita-image-store.s3.amazonaws.com/0/28302/a2259e06-c25a-19bc-2f84-c01de631b411.jpeg)

青色が JDK 付属のインターフェース。緑色が GS Collections で定義されているインターフェースやクラス。

`MutableList` は、 JDK の `List` インターフェースを継承しているため、 `add()` などの更新系メソッドを持つ。
一方で、 `ImmutableList` は JDK で定義されているインターフェースのうち、 `Iterable` しか継承していない。そして親インターフェースである `ListIterable` や、さらにその親達のインターフェースには、更新系のメソッドが定義されていない。このため、 `ImmutableList` 型の変数を定義すると、更新が一切できない `List ` ができあがる。

一応、JDK でも `Collections.unmodifiableList()` などのメソッドを使うことで Immutable なコレクションを作成できる。
しかし、このメソッドは戻り値の型が `List` になっている。このため `add()` などの更新系のメソッドを呼ぶことができてしまう。その場合、コンパイルエラーは起こらず、実行時に `UnsupportedOperationException` がスローされる。
つまり、間違って更新系のメソッドを呼んでしまっていても、実行するまでミスに気づけないという問題点がある。

一方、 `ImmutableList` ならそもそも更新系のメソッドが定義されていないので、ミスすることはない（コンパイルエラーになる）。
また、型も `Immutable` と銘打たれているので、値の使い回しが容易になったり、マルチスレッドプログラミングで安心して利用できるといったメリットがある（`List` 型という情報だけだと Imutable かどうかわからず、使い回しやマルチスレッドでの利用がしづらくなる）。

#どの型の変数に代入すべきか
JDK のコレクションフレームワークでは、リストなら `List` 型に、セットなら `Set` 型に、マップなら `Map` 型の変数に代入するのが良いとされていた。

GS Collections ならどうすべきか。
上述の Mutable, Immutable の関係を考えるに、以下のようになるかと思う（個人的な見解です）。

```text:
List, Set, Map などコレクションの具体的な種類を意識する？
 |
 +-- <NO> -->変更可能かどうか意識する？
 |            |
<YES>         +-- <NO> --> RichIterable
 |            |
 |            +-- <YES> --> MutableCollection, ImmutableCollection
 |            
変更可能かどうか意識する？
 |
 +-- <NO> --> ListIterable, SetIterable, MapIterable...
 |
 +-- <YES> --> MutableList, MutableSet, MutableMap...
               ImmutableList, ImmutableSet, ImmutableMap...
```

#基本的なクラスを使う
JDK の `ArrayList`、`HashSet`、`HashMap` に当たるクラスは、 GS Collections では `FastList`、`UnifiedSet`、`UnifiedMap` になる。

```java:
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.gs.collections.impl.list.mutable.FastList;
import com.gs.collections.impl.map.mutable.UnifiedMap;
import com.gs.collections.impl.set.mutable.UnifiedSet;

public class Main {
    public static void main(String[] args) {
        List<String> list = new FastList<>();
        Set<String> set = new UnifiedSet<>();
        Map<String, String> map = new UnifiedMap<>();
    }
}
```

これらのクラスには、 static なファクトリメソッドが用意されている。

```java:ファクトリメソッドを用いた初期化
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.gs.collections.impl.list.mutable.FastList;
import com.gs.collections.impl.map.mutable.UnifiedMap;
import com.gs.collections.impl.set.mutable.UnifiedSet;
import com.gs.collections.impl.tuple.Tuples;

public class Main {

    @SuppressWarnings("unchecked")
    public static void main(String[] args) {
        List<String> list = FastList.newListWith("hoge", "fuga", "piyo");
        Set<String> set = UnifiedSet.newSetWith("hoge", "fuga", "piyo");
        
        Map<String, String> map = UnifiedMap.newWithKeysValues(
                                                "hoge", "HOGE",
                                                "fuga", "FUGA",
                                                "piyo", "PIYO");

        map = UnifiedMap.newMapWith(
                Tuples.pair("hoge", "HOGE"),
                Tuples.pair("fuga", "FUGA"),
                Tuples.pair("piyo", "PIYO"));
    }
}
```

`UnifiedMap` は `newWithKeysValues(key1, value1, key2, value2)` という形で初期化できるが、エントリの数が４つまでしか対応していない。
それより多い場合は `newMapWith(Pair...)` を使う。

また、各コレクションにはファクトリ用のクラスが用意されていて、それを利用することもできる。

```java:
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.gs.collections.impl.factory.Lists;
import com.gs.collections.impl.factory.Maps;
import com.gs.collections.impl.factory.Sets;

public class Main {

    public static void main(String[] args) {
        List<String> list = Lists.mutable.of("hoge", "fuga", "piyo");
        Set<String> set = Sets.mutable.of("hoge", "fuga", "piyo");
        Map<String, String> map = Maps.mutable.of("hoge", "HOGE", "fuga", "FUGa", "piyo", "PIYO");
    }
}
```

#基本的なイテレーションパターン
##select
```java:
import java.util.List;

import com.gs.collections.impl.factory.Lists;

public class Main {

    public static void main(String[] args) {
        List<String> list = Lists.mutable
                                 .of("hoge", "fuga", "piyo")
                                 .select(s -> s.contains("g"));
        
        System.out.println(list);
    }
}
```

```text:実行結果
[hoge, fuga]
```

- 条件に一致する要素だけを抽出する。

##reject
```java:
import java.util.List;

import com.gs.collections.impl.factory.Lists;

public class Main {

    public static void main(String[] args) {
        List<String> list = Lists.mutable
                                 .of("hoge", "fuga", "piyo")
                                 .reject(s -> s.contains("g"));
        
        System.out.println(list);
    }
}
```

```text:実行結果
[piyo]
```

- 条件に一致しない要素だけを抽出する。

##partition
```java:
import com.gs.collections.api.partition.list.PartitionMutableList;
import com.gs.collections.impl.factory.Lists;

public class Main {

    public static void main(String[] args) {
        PartitionMutableList<String> list = Lists.mutable
                                                 .of("hoge", "fuga", "piyo")
                                                 .partition(s -> s.contains("g"));
        
        System.out.println(list.getSelected());
        System.out.println(list.getRejected());
    }
}
```

```text:実行結果
[hoge, fuga]
[piyo]
```

- 条件に一致した要素と、そうでない要素とで分割する。

##collect
```java:
import java.util.List;

import com.gs.collections.impl.factory.Lists;

public class Main {

    public static void main(String[] args) {
        List<Integer> list = Lists.mutable
                                  .of("one", "two", "three")
                                  .collect(s -> s.length());
        
        System.out.println(list);
    }
}
```

```text:実行結果
[3, 3, 5]
```

- 各要素を変換して、新しい List を生成する。

##flatCollect
```java:
import java.util.List;

import com.gs.collections.impl.factory.Lists;
import com.gs.collections.impl.list.mutable.FastList;
import com.gs.collections.impl.tuple.Tuples;

public class Main {

    @SuppressWarnings("unchecked")
    public static void main(String[] args) {
        List<String> list = Lists.mutable
                                 .of(Tuples.pair("hoge", "HOGE"),
                                     Tuples.pair("fuga", "FUGA"),
                                     Tuples.pair("piyo", "PIYO"))
                                 .flatCollect(pair -> FastList.newListWith(pair.getOne(), pair.getTwo()));
        
        System.out.println(list);
    }
}
```

```text:
[hoge, HOGE, fuga, FUGA, piyo, PIYO]
```

- コレクションの各要素に対して `flatCollect()` に渡したラムダ式が適用される。
- ラムダ式は `Iterable` を実装した値を返すようにする。
- すると、ラムダ式が返した `Iterable` をすべて連結した新しい `List` が返される。
- つまり、フラットな状態にしている。

##groupBy
```java:
import com.gs.collections.api.multimap.Multimap;
import com.gs.collections.impl.factory.Lists;

public class Main {

    public static void main(String[] args) {
        Multimap<Integer, String> map = Lists.mutable
                                             .of("one", "two", "three", "four", "five")
                                             .groupBy(s -> s.length());
        System.out.println(map);
    }
}
```

```text:実行結果
{3=[one, two], 4=[four, five], 5=[three]}
```

- `groupBy()` に渡したラムダ式が返した値で、コレクションの各要素を集約する。
- 戻り値は `Multimap` で返される（キー１つに対して、複数の値が設定できる `Map`）。

##detect
```java:
import com.gs.collections.api.list.ListIterable;
import com.gs.collections.impl.factory.Lists;

public class Main {

    public static void main(String[] args) {
        ListIterable<String> list = Lists.mutable.of("one", "two", "three");
        
        System.out.println(list.detect(s -> s.length() == 3));
        System.out.println(list.detect(s -> s.length() == 6));
    }
}
```

```text:実行結果
one
null
```

- `detect()` の引数で渡したラムダ式がコレクションの各要素に対して適用される。
- １回でも `true` を返した場合は、最初に `true` を返した要素が返される。
- １回も `true` が返されない場合は、 `null` が返される。

##detectIfNone
```java:
import com.gs.collections.api.list.ListIterable;
import com.gs.collections.impl.factory.Lists;

public class Main {

    public static void main(String[] args) {
        ListIterable<String> list = Lists.mutable.of("one", "two", "three");
        
        System.out.println(list.detectIfNone(s -> s.length() == 3, () -> "default value"));
        System.out.println(list.detectIfNone(s -> s.length() == 6, () -> "default value"));
    }
}
```

```text:実行結果
one
default value
```

- 第一引数のラムダ式がすべての要素に対して `false` を返した場合、第二引数のラムダ式が返す値が返される。

##anySatisfy
```java:
import com.gs.collections.api.list.ListIterable;
import com.gs.collections.impl.factory.Lists;

public class Main {

    public static void main(String[] args) {
        ListIterable<String> list = Lists.mutable.of("one", "two", "three");
        
        System.out.println(list.anySatisfy(s -> s.contains("r")));
        System.out.println(list.anySatisfy(s -> s.contains("z")));
    }
}
```

```text:実行結果
true
false
```

- １回でもラムダ式が `true` を返したら、 `true` を返す。

##allSatisfy
```java:
import com.gs.collections.api.list.ListIterable;
import com.gs.collections.impl.factory.Lists;

public class Main {

    public static void main(String[] args) {
        ListIterable<String> list = Lists.mutable.of("hoge", "fuga", "piyo");
        
        System.out.println(list.allSatisfy(s -> s.length() == 4));
        System.out.println(list.allSatisfy(s -> s.contains("o")));
    }
}
```

```text:実行結果
true
false
```

- すべての要素に対してラムダ式が `true` を返したときだけ、 `true` を返す。

##noneSatisfy
```java:
import com.gs.collections.api.list.ListIterable;
import com.gs.collections.impl.factory.Lists;

public class Main {

    public static void main(String[] args) {
        ListIterable<String> list = Lists.mutable.of("hoge", "fuga", "piyo");
        
        System.out.println(list.noneSatisfy(s -> s.length() == 5));
        System.out.println(list.noneSatisfy(s -> s.contains("o")));
    }
}
```

```text:実行結果
true
false
```

- すべての要素に対して、ラムダ式が `false` を返した場合のみ、 `true` を返す。

##forEach
```java:
import com.gs.collections.api.list.ListIterable;
import com.gs.collections.impl.factory.Lists;

public class Main {

    public static void main(String[] args) {
        ListIterable<String> list = Lists.mutable.of("hoge", "fuga", "piyo");
        
        list.forEach(System.out::println);
    }
}
```

```text:実行結果
※コンパイルエラーで実行できない
```

- `forEach()` は、JDK の `Iterable` と GS Collections の `InternalIterable` の両方で定義されてしまっている。
- ２つのメソッドは、受け取る引数の型は違うものの、ラムダ式で記述すると全く同じ実装になってしまう。
- このため、コンパイラはメソッドを２つのどちらかを区別することができず、コンパイルエラーになる。
- Java 8 以降の環境でラムダ式を使う場合は、変わりに `each()` メソッドを使う。

##forEachWithIndex
```java:
import com.gs.collections.api.list.ListIterable;
import com.gs.collections.impl.factory.Lists;

public class Main {

    public static void main(String[] args) {
        ListIterable<String> list = Lists.mutable.of("hoge", "fuga", "piyo");
        
        list.forEachWithIndex((s, i) -> System.out.printf("[%d] : %s%n", i, s));
    }
}
```

```text:実行結果
[0] : hoge
[1] : fuga
[2] : piyo
```

- インデックスとともに内部ループできる。
- これは地味にありがたい（標準の API だけだと、インデックスが必要になった途端初代 for 文に舞い戻る必要があったので）。

##injectInto
```java:
import com.gs.collections.impl.factory.Lists;

public class Main {

    public static void main(String[] args) {
        String result = Lists.mutable.of(1, 2, 3, 4, 5)
                             .injectInto("0", (memo, element) -> {
                                 System.out.printf("element=%d, memo=\"%s\"%n", element, memo);
                                 return memo + " - " + element;
                             });
        
        System.out.println(result);
    }
}
```

```text:実行結果
element=1, memo="0"
element=2, memo="0 - 1"
element=3, memo="0 - 1 - 2"
element=4, memo="0 - 1 - 2 - 3"
element=5, memo="0 - 1 - 2 - 3 - 4"
0 - 1 - 2 - 3 - 4 - 5
```

- 第一引数が `memo` の初期値となり、第二引数のラムダ式がコレクションの各要素に対して適用される。
- ラムダ式の第一引数には、最初は `injectInto()` メソッドの第一引数の値が、以後はラムダ式が直前に `return` した値が渡される。
- ラムダ式の第二引数には、コレクションの各要素が順次渡される。

##tap
```java
import com.gs.collections.impl.factory.Lists;

public class Main {

    public static void main(String[] args) {
        Lists.mutable
             .of(1, 2, 3, 4)
             .tap(n -> System.out.println("[" + n + "]"))
             .select(n -> n % 2 == 0)
             .each(n -> System.out.println("<" + n + ">"));
    }
}
```

```text:実行結果
[1]
[2]
[3]
[4]
<2>
<4>
```

- `each()` は戻り値が `void` なのに対して、 `tap` は戻り値が再び `RichIterable` を返すようになっている。
- つまり、要素全体に対して何らかの処理を行った後も処理を続けることができる。

#遅延反復(Lazy iteration)
各イテレーション用のメソッドは、実行するたびに反復処理が実行される。

```java:
import com.gs.collections.impl.factory.Lists;

public class Main {

    public static void main(String[] args) {
        Lists.mutable
             .of("one", "two", "three")
             .select(s -> {
                 System.out.println("select : " + s);
                 return s.contains("t");
             })
             .collect(s -> {
                 System.out.println("collect : " + s);
                 return s.length();
             })
             .each(l -> {
                 System.out.println("each : " + l);
             });
    }
}
```

```text:実行結果
select : one
select : two
select : three
collect : two
collect : three
each : 3
each : 5
```

一方、 Java 8 の Stream API は終端処理を実行するまでループは実行されない遅延反復になっている。

GS Collections でも、以下のように実装することで遅延反復が実現できる。

```diff:GSCollectionsでの遅延反復
import com.gs.collections.impl.factory.Lists;

public class Main {

    public static void main(String[] args) {
        Lists.mutable
             .of("one", "two", "three")
+            .asLazy()
             .select(s -> {
                 System.out.println("select : " + s);
                 return s.contains("t");
             })
             .collect(s -> {
                 System.out.println("collect : " + s);
                 return s.length();
             })
             .each(l -> {
                 System.out.println("each : " + l);
             });
    }
}
```

```text:実行結果
select : one
select : two
collect : two
each : 3
select : three
collect : three
each : 5
```

`asLazy()` と入れるだけで、遅延反復になる。

もうちょい分かりやすく。。。

```java:
import com.gs.collections.api.RichIterable;
import com.gs.collections.impl.factory.Lists;

public class Main {

    public static void main(String[] args) {
        RichIterable<String> list1 = Lists.mutable.of("one", "two", "three").asLazy();
        
        System.out.println(">>>>>>>>>>");
        RichIterable<String> list2 = list1.select(s -> {
            System.out.println("select : " + s);
            return s.contains("t");
        });
        
        
        System.out.println(">>>>>>>>>>");
        RichIterable<Integer> list3 = list2.collect(s -> {
            System.out.println("collect : " + s);
            return s.length();
        });
        
        
        System.out.println(">>>>>>>>>>");
        list3.each(l -> System.out.println("each : " + l));
    }
}
```

```text:実行結果
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
select : one
select : two
collect : two
each : 3
select : three
collect : three
each : 5
```

最後に `each()` を実行した時に、初めて `select()` や `collect()` で渡したラムダ式が実行されている。

#その他の便利なメソッド
##makeString
```java:
import com.gs.collections.api.RichIterable;
import com.gs.collections.impl.factory.Lists;

public class Main {

    public static void main(String[] args) {
        RichIterable<Integer> ite = Lists.mutable.of(1, 2, 3, 4);
        
        System.out.println(ite.makeString());
        System.out.println(ite.makeString(" : "));
        System.out.println(ite.makeString("[", ", ", "]"));
    }
}
```

```text:実行結果
1, 2, 3, 4
1 : 2 : 3 : 4
[1, 2, 3, 4]
```

- `makeString()` で、半角カンマ区切り、
- `makeString(String)` で、区切り文字指定、
- `makeString(String, String, String)` で、開始・終了文字と区切り文字指定、
- で `Iterable` 内部の要素を連結した文字列を生成できる。

##appendString
```java:
import java.io.StringWriter;

import com.gs.collections.api.RichIterable;
import com.gs.collections.impl.factory.Lists;

public class Main {

    public static void main(String[] args) {
        RichIterable<Integer> ite = Lists.mutable.of(1, 2, 3, 4);
        
        StringWriter sw = new StringWriter();
        ite.appendString(sw, "{", " * " , "}");
        
        System.out.println(sw);
    }
}
```

```text:実行結果
{1 * 2 * 3 * 4}
```

- 第一引数でした [Appendable][5] に、 `makeString()` と同じ要領で作成した文字列を追記する。
- `PrintStream` や `Writer`、 `StringBuilder` などが `Appendable` を実装している。

##count
```java:
import com.gs.collections.impl.factory.Lists;

public class Main {

    public static void main(String[] args) {
        System.out.println(Lists.mutable.of(1, 2, 3, 4).count(n -> n % 2 == 0));
        
    }
}
```

```text:実行結果
2
```

- 引数で指定した条件を満たす要素の数を返す。
- 単純に要素数を取得したいときは `size()` メソッドがある。

##getFirst/getLast
```java:
import com.gs.collections.api.ordered.OrderedIterable;
import com.gs.collections.impl.factory.Lists;

public class Main {

    public static void main(String[] args) {
        OrderedIterable<Integer> list = Lists.mutable.of(1, 2, 3, 4);
        
        System.out.println(list.getFirst());
        System.out.println(list.getLast());
    }
}
```

```text:実行結果
1
4
```

- 先頭と末尾の要素を取得できる。

##max/min
```java:
import com.gs.collections.api.RichIterable;
import com.gs.collections.impl.factory.Lists;

public class Main {

    public static void main(String[] args) {
        RichIterable<Integer> list = Lists.mutable.of(1, 2, 3, 4);
        
        System.out.println(list.max());
        System.out.println(list.min());
    }
}
```

```text:実行結果
4
1
```

- 最大と最小の要素を取得できる。
- 引数に [Comparator][6] を渡すことで、比較方法を指定することもできる。

##maxBy/minBy
```java:
import com.gs.collections.api.RichIterable;
import com.gs.collections.impl.factory.Lists;

public class Main {

    public static void main(String[] args) {
        RichIterable<String> list = Lists.mutable.of("one", "two", "three");
        
        System.out.println(list.max());
        System.out.println(list.maxBy(s -> s.length()));
    }
}
```

```text:実行結果
two
three
```

- 引数で指定したラムダ式が返した値で順序付けを行い、最大 / 最小の要素を取得する。

##aggregateBy
```java:
import java.util.List;

import com.gs.collections.api.map.MapIterable;
import com.gs.collections.impl.factory.Lists;

public class Main {

    public static void main(String[] args) {
        MapIterable<String, List<String>> map = 
                Lists.mutable
                     .of(1, 2, 3, 4, 5, 6)
                     .aggregateBy(
                         n -> (n % 2 == 0) ? "even" : "odd",
                         () -> Lists.mutable.empty(),
                         (list, n) -> {
                             list.add(String.valueOf(n));
                             return list;
                         }
                     );
        
        System.out.println(map);
    }
}
```

```text:実行結果
{even=[2, 4, 6], odd=[1, 3, 5]}
```

- 要素をグループ分けして、新しい `Map` を生成する。
- 第一引数が、グループ分けをするためのラムダ式。このラムダ式が返した値で要素がグループ分けされる。
- 第二引数が、グループごとの初期値を生成するラムダ式。第一引数が新しいグループの分類を返すたびにコールバックされる。
- 第三引数が、グループごとの値を作るためのラムダ式。
    - ラムダ式の第一引数には、そのグループが出来た直後の場合は `aggregateBy()` の第二引数のラムダ式が返した初期値が、
    - その後は、前回このラムダ式が返した値が渡される。
    - 第二引数には、要素の値が順次渡される。

##chunk
```java:
import com.gs.collections.impl.factory.Lists;

public class Main {

    public static void main(String[] args) {
        System.out.println(Lists.mutable.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).chunk(4));
    }
}
```

```text:実行結果
[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10]]
```

- 指定した数ずつになるように要素をできる限り当分割して、分割されてできたコレクションを要素として持つ新しいコレクションを返す。

##zip
```java:
import java.util.List;

import com.gs.collections.api.tuple.Pair;
import com.gs.collections.impl.factory.Lists;

public class Main {

    public static void main(String[] args) {
        List<Pair<String, Integer>> list = Lists.mutable
                                                .of("one", "two", "three", "four")
                                                .zip(Lists.mutable.of(9, 8, 7));
        
        System.out.println(list);
    }
}
```

```text:実行結果
[one:9, two:8, three:7]
```

- コレクション同士の各要素を順番に突き合わせて、それぞれの要素を `Pair` に格納し、できた `Pair` を新しいリストに詰めて返す。
- 要素数が合わない場合は、小さい方に合わされる。

##take/drop
```java
import com.gs.collections.impl.factory.Lists;

public class Main {

    public static void main(String[] args) {
        System.out.println(Lists.mutable.of(1, 2, 3, 4).take(2));
        System.out.println(Lists.mutable.of(1, 2, 3, 4).drop(2));
    }
}
```

```text:実行結果
[1, 2]
[3, 4]
```

- `take()` で、指定した数だけ先頭から要素を抽出する。
- `drop()` は後ろから抽出する。

##distinct
```java:
import com.gs.collections.impl.factory.Lists;

public class Main {

    public static void main(String[] args) {
        System.out.println(Lists.mutable.of(1, 1, 2, 3, 3, 4).distinct());
    }
}
```

```text:実行結果
[1, 2, 3, 4]
```

- 重複を取り除く。

##with/without
```java:
import com.gs.collections.impl.factory.Lists;

public class Main {

    public static void main(String[] args) {
        Lists.mutable
             .of(1, 2, 3)
             .without(2)
             .with(99)
             .each(System.out::println);
    }
}
```

```text:実行結果
1
3
99
```

- `with()` は要素を追加、 `without()` は要素を削除。
- `add()` や `remove()` との違いは、流れるようなインターフェースの上で使える、という点。
- `Mutable` なコレクションの場合、実際に要素が追加・削除されている（新しいコレクションが生成されているわけではない）。

#追加のコレクション
##プリミティブコレクション
```java
import com.gs.collections.api.IntIterable;
import com.gs.collections.impl.factory.primitive.IntLists;

public class Main {

    public static void main(String[] args) {
        IntIterable ite = IntLists.mutable.of(1, 2, 3);
    }
}
```

- `Integer` などのラッパークラスを使用していると、オートボクシングが発生したりでパフォーマンス的によろしくない。
- ということで、プリミティブ型専用の `Iterable` が用意されている。
- 各プリミティブごとに用意されている（例は `int` 型のケース）。

##Bag
```java:
import com.gs.collections.api.bag.Bag;
import com.gs.collections.impl.factory.Bags;

public class Main {

    public static void main(String[] args) {
        Bag<String> bag = Bags.mutable.of("hoge", "fuga", "fuga", "piyo", "piyo", "piyo");
        
        System.out.printf("%s は %d 個%n", "fuga", bag.occurrencesOf("fuga"));
        
        bag.forEachWithOccurrences((s, n) -> System.out.printf("%s : %d 個%n", s, n));
        
        System.out.println(bag.toMapOfItemToCount());
    }
}
```

```text:実行結果
fuga は 2 個
fuga : 2 個
hoge : 1 個
piyo : 3 個
{hoge=1, fuga=2, piyo=3}
```

- 要素の重複が許可された `Set` のようなもの。
- 重複して登録された要素については、その出現回数を取得することができる。
- `occurrencesOf(要素)` で指定した要素の出現回数を取得できる。
- `forEachWithOccurrences()` で要素と出現回数を引数に受け取りながら反復処理ができる・
- `toMapOfItemToCount()` で、キーに要素、値に出現回数を持った `Map` に変換できる。

##Multimap
```java:
import com.gs.collections.api.multimap.MutableMultimap;
import com.gs.collections.impl.factory.Multimaps;

public class Main {

    public static void main(String[] args) {
        System.out.println("set  : " + setup(Multimaps.mutable.set.empty()));
        System.out.println("list : " + setup(Multimaps.mutable.list.empty()));
        System.out.println("bag  : " + setup(Multimaps.mutable.bag.empty()));
    }
    
    private static MutableMultimap<String, String> setup(MutableMultimap<String, String> map) {
        map.put("one", "xxxx");
        map.put("one", "yyyy");
        map.put("one", "zzzz");
        map.put("one", "xxxx");
        map.put("two", "yyyy");
        map.put("two", "xxxx");
        
        return map;
    }
}
```

```text:実行結果
set  : {two=[xxxx, yyyy], one=[xxxx, yyyy, zzzz]}
list : {two=[yyyy, xxxx], one=[xxxx, yyyy, zzzz, xxxx]}
bag  : {two=[xxxx, yyyy], one=[xxxx, xxxx, yyyy, zzzz]}
```

- １つのキーに対して、複数の値を登録できる `Map`。
- 要は、 `Map<キー, Collection>` な `Map` のイメージ。
- 値を保存するコレクションは `Set`, `List`, `Bag` のいずれかを選べる。
    - `Set` の場合、値は重複を取り除かれた状態で保存される。
    - `List` と `Bag` は、値が重複したまま保存される。
    - `List` は登録した順序も保存されるが、 `Bag` は順序が保存されない。



#参考
- [Overview (GS Collections - 6.0.0)](http://www.goldmansachs.com/gs-collections/javadoc/6.0.0/index.html)
- [GS Collections User Reference Guide 5.0.0](http://www.goldmansachs.com/gs-collections/documents/GS%20Collections%20Reference%20Guide%205.0.0.pdf)
- [実例で学ぶGS Collections – Part 1](http://www.infoq.com/jp/articles/GS-Collections-by-Example-1)
- [遅延評価いうなキャンペーンとかどうか - ぐるぐる～](http://bleis-tift.hatenablog.com/entry/20130102/1357062031)



[1]: http://www.goldmansachs.com/gs-collections/presentations/2015-04-08_JavaDayTokyo.pdf
[2]: http://www.goldmansachs.com/gs-collections/presentations/2015-04-11_JJUG_CCC.pdf
[3]: http://www.oracle.com/webfolder/technetwork/jp/javamagazine/Java-MA14-JCP-GoldmanSachs.pdf
[4]: http://www.goldmansachs.com/gs-collections/javadoc/6.0.0/com/gs/collections/impl/list/mutable/FastList.html
[5]: http://docs.oracle.com/javase/jp/8/docs/api/java/lang/Appendable.html
[6]: http://docs.oracle.com/javase/jp/8/docs/api/java/util/Comparator.html