JavaにC#のLINQを移植してみた - jLinqer

#JavaでLINQが使いたい

　Javaで使えるLINQライブラリを作成しました。C#のLINQなら流暢に表現できるのに，Java8のStreamでは上手く表現できない悩みをラップします。

#jLinqer

　**jLinqer**はJavaでLINQ (Language INtegrated Query: 統合言語クエリ) が使えるライブラリです。C#のLINQに存在してJava8のStreamに不足している機能(selectMany，Union, Intersect, Except等)も追加しています。

## 使用方法

　Gradleで使用する場合は，以下を登録してください。

```js
'com.github.jlinqer:jlinqer:1.0.0'
```

　Mavenで使用する場合は，以下のpom.xmlを登録してください。

```xml

<dependency>
    <groupId>com.github.jlinqer</groupId>
    <artifactId>jlinqer</artifactId>
    <version>1.0.0</version>
</dependency>

```

　mavenを使用せず，jLinqerのjarを直接使用する場合は，以下よりダウンロードして下さい。

　　‡Maven, "jLinqer", https://oss.sonatype.org/content/groups/public/com/github/jlinqer/jlinqer/1.0.0/jlinqer-1.0.0.jar
　

##LINQ - jLinqer 対応マトリクス

| 機能 | LINQ(C#) | jLinqer(Java) | Stream(Java) |
|-----|------|-------|--------|
| **【基本】** ||||
| 抽出 | Where | [where](#where) | filter |
| 射影 | Select | [select](#select) | map |
| 並べ替え(昇順) | OrderBy | [orderBy](#orderby) | sorted |
| 並べ替え(降順) | OrderByDescending | [orderByDescending](#orderbydescending) | n/a |
| 後続を並べ替え(昇順) | ThenBy | [thenBy](#thenby) | n/a |
| 後続を並べ替え(降順) | ThenByDescending | [thenByDescending](#thenbydescending) | n/a |
| 平坦化して射影 | SelectMany | [selectMany](#selectmany) | flatMap |
| **【抽出系】** ||||
| ｎ件飛ばす | Skip | [skip](#skip) | skip |
| 条件を満たすまで飛ばす | SkipWhile | [skipWhile](#skipwhile) | n/a |
| ｎ件まで流す | Take | [take](#take) | limit | 
| 条件を満たすまで流す | TakeWhile | [takeWhile](#takewhile) | n/a |
| **【合成系】** ||||
| 連結 | Concat | [concat](#concat) | concat |
| 積集合 | Intersect | [intersect](#intersect) | n/a |
| 和集合 | Union | [union](#union) | n/a |
| 差集合 | Except | [except](#except) | n/a |
| 内部結合 | Join | [join](#join) | n/a |
| 外部結合| GroupJoin | [groupJoin](#groupjoin) | n/a |
| 並びを逆にする | Reverse | [reverse](#reverse) | n/a |
| 2つの値を揃えて流す | Zip | [zip](#zip) | n/a |
| **【グループ化、集計系】** |  |  | |
| 重複を無くす | Distinct | [distinct](#distinct) | distinct |
| 畳み込み | Aggregate | [aggregate](#aggregate) | reduce |
| グループ化 | GroupBy | [groupBy](#groupby) | Collectors.groupingBy |
| 平均 | Average | [averageXXX](#average) | Collectors.summarizingXXX |
| 件数 | Count | [count](#count) | n/a |
| 件数 | LongCount | [longCount](#count) | count |
| 最大 | Max | [max](#max) | max |
| 最小 | Min | [min](#min) | min |
| 合計 | Sum | [sumXXX](#sum) | Collectors.summarizingXXX |
| 先頭 | First | [first](#firstordefault) | findFirst |
| 先頭 | FirstOrDefault | [firstOrDefault](#firstordefault) | n/a |
| 終端 | Last | [last](#lastordefault) | n/a |
| 終端 | LastOrDefault | [lastOrDefault](#lastordefault) | n/a |
| 1件の値を得る | Single | [single](#singleordefault) | n/a |
| 1件の値を得る | SingleOrDefault | [singleOrDefault](#singleordefault) | n/a |
| 空なら既定値を返す | DefaultIfEmpty | [defaultIfEmpty](#defaultifempty) | n/a |
| 指定番目の要素取得 | ElementAt | [elementAt](#elementatordefault) | n/a |
| 指定番目の要素取得 | ElementAtOrDefault | [elementAtOrDefault](#elementatordefault) | n/a |
| 全データが条件にマッチするか？ | All | [all](#all) | allMatch |
| いずれかのデータが条件にマッチするか？ | Any | [any](#any) | anyMatch |
| **【生成系】** |  |  |
| 空 | Empty | [empty](#empty) | n/a |
| 範囲を生成 | Range | [range](#range) | n/a |
| 繰り返す | Repeat | [repeat](#repeat) | n/a |
| **【その他】** |  |  |
| 全要素一致 | SequenceEqual | [sequenceEqual](#sequenceequal) | n/a |
| キャスト | Cast | [cast](#cast) | n/a |
| 一致する型のみ抽出 | OfType | [ofType](#oftype) | n/a |

##使い方
 
 java.util.Listをcom.github.jlinqer.collections.Listに置き換えて使用します。

### Where

```Java
List<Integer> list = new List<>(1, 2, 3);

List<Integer> actual = list.where(x -> x == 1 || x == 3).toList();

assertEquals(true , actual.contains(1));
assertEquals(false, actual.contains(2));
assertEquals(true , actual.contains(3));
```

### Select

```Java
List<Person> list = new List<>(
        new Person("React"   , 1),
        new Person("Angular" , 3),
        new Person("Backbone", 5)
);

List<String> actual = list.select(x -> x.name).toList();

assertEquals("React"   , actual.get(0));
assertEquals("Angular" , actual.get(1));
assertEquals("Backbone", actual.get(2));
```

### OrderBy

```Java
List<String> list = new List<>("Backbone", "Angular", "React");

List<String> actual = list.orderBy(x -> x).toList();

assertEquals("Angular" , actual.get(0));
assertEquals("Backbone", actual.get(1));
assertEquals("React"   , actual.get(2));
```

### OrderByDescending

```Java
List<String> list = new List<>("Backbone", "Angular", "React");

List<String> actual = list.orderByDescending(x -> x).toList();

assertEquals("React"   , actual.get(0));
assertEquals("Backbone", actual.get(1));
assertEquals("Angular" , actual.get(2));
```

### ThenBy

```Java
List<Person> list = new List<>(
        new Person("Angular2", 2),
        new Person("Angular1", 2),
        new Person("React"   , 1)
);

List<String> actual = list.orderBy(x -> x.age).thenBy(x -> x.name).toList();

assertEquals("React" , actual.get(0).name);
assertEquals("Angular1", actual.get(1).name);
assertEquals("Angular2"   , actual.get(2).name);
```

### ThenByDescending

```Java
List<Person> list = new List<>(
        new Person("Angular2", 2),
        new Person("Angular1", 2),
        new Person("React"   , 1)
);

List<String> actual = list.orderBy(x -> x.age).thenByDescending(x -> x.name).toList();

assertEquals("React" , actual.get(0).name);
assertEquals("Angular2", actual.get(1).name);
assertEquals("Angular1"   , actual.get(2).name);
```

### SelectMany

```Java
List<Person> list = new List<>(
        new Person("Angular", 3, new List("1.0.1", "1.0.2")),
        new Person("React"  , 1, new List("2.0.1", "2.0.2"))
);

List<String> actual = list.selectMany(x -> x.versionHistory).toList();

assertEquals("1.0.1", actual.get(0));
assertEquals("1.0.2", actual.get(1));
assertEquals("2.0.1", actual.get(2));
assertEquals("2.0.2", actual.get(3));
```

### Skip

```Java
List<Integer> list = new List<>(1, 2, 3);

List<Integer> actual = list.skip(2).toList();

assertEquals(3, actual.get(0).intValue());
```

### SkipWhile

```Java
List<Integer> list = new List<>(1, 2, 3, 4, 5);

List<Integer> actual = list.skipWhile(x -> x <= 3).toList();

assertEquals(4, actual.get(0).intValue());
assertEquals(5, actual.get(1).intValue());
```

### Take

```Java
List<String> list = new List<>("Backbone", "Angular", "React");

List<String> actual = list.take(2).toList();

assertEquals(2, actual.size());
assertEquals("Backbone", actual.get(0));
assertEquals("Angular" , actual.get(1));
```

### TakeWhile

```Java
List<String> list = new List<>("Backbone", "Angular", "React");

List<String> actual = list.takeWhile(x -> x.equals("Backbone") || x.equals("Angular")).toList();

assertEquals(2, actual.size());
assertEquals("Backbone", actual.get(0));
assertEquals("Angular" , actual.get(1));
```
### Concat

```Java
List<Integer> first  = new List<>(1, 2);
List<Integer> second = new List<>(2, 3);

List<Integer> actual = first.concat(second).toList();

assertEquals(1, actual.get(0).intValue());
assertEquals(2, actual.get(1).intValue());
assertEquals(2, actual.get(2).intValue());
assertEquals(3, actual.get(3).intValue());
```

### Intersect

```Java
List<Integer> first  = new List<>(1, 2, 3);
List<Integer> second = new List<>(1, 3);

List<Integer> actual = first.intersect(second).toList();

assertEquals(1, actual.get(0).intValue());
assertEquals(3, actual.get(1).intValue());
```

### Union

```Java
List<Integer> first = new List<>(1, 2, 3);
List<Integer> second = new List<>(0, 1, 3, 4);

List<Integer> actual = first.union(second).toList();

assertEquals(5, actual.size());
assertEquals(1, actual.get(0).intValue());
assertEquals(2, actual.get(1).intValue());
assertEquals(3, actual.get(2).intValue());
assertEquals(0, actual.get(3).intValue());
assertEquals(4, actual.get(4).intValue());
```

### Except

```Java
List<Integer> first  = new List<>(1, 2, 3);
List<Integer> second = new List<>(1, 3);

List<Integer> actual = first.except(second).toList();

assertEquals(2, actual.get(0).intValue());
```


### Join

```Java
List<Javascript> outer = new List<>(
        new Javascript("Angular", 1),
        new Javascript("React"  , 4),
        new Javascript("ES2016" , 5)
);
List<Javascript> inner = new List<>(
        new Javascript("Angular", 2),
        new Javascript("Angular", 3),
        new Javascript("ES2016" , 6),
        new Javascript("ES7"    , 7)
);

Function<Javascript, String> outerKey = (x) -> x.name;
Function<Javascript, String> innerKey = (y) -> y.name;
BiFunction<Javascript, Javascript, Javascript> selector = (x, y) -> new Javascript(x.name, y.age);
List<Javascript> actual = outer.join(inner, outerKey, innerKey, selector).toList();

assertEquals(3, actual.size());
assertEquals("Angular", actual.get(0).name);
assertEquals("Angular", actual.get(1).name);
assertEquals("ES2016" , actual.get(2).name);
assertEquals(2, actual.get(0).age);
assertEquals(3, actual.get(1).age);
assertEquals(6, actual.get(2).age);
```

### GroupJoin

```Java
List<Javascript> outer = new List<>(
        new Javascript("Angular", 1),
        new Javascript("React"  , 4),
        new Javascript("ES2016" , 5)
);
List<Javascript> inner = new List<>(
        new Javascript("Angular", 2),
        new Javascript("Angular", 3),
        new Javascript("ES2016" , 6),
        new Javascript("ES7"    , 7)
);

Function<Javascript, String> outerKey = (x) -> x.name;
Function<Javascript, String> innerKey = (y) -> y.name;
BiFunction<Javascript, IEnumerable<Javascript>, Javascript> selector = (x, y) -> new Javascript(x.name, y.select(z -> z.age));
List<Javascript> actual = outer.groupJoin(inner, outerKey, innerKey, selector).toList();

assertEquals(3, actual.size());
assertEquals("Angular", actual.get(0).name);
assertEquals("React"  , actual.get(1).name);
assertEquals("ES2016" , actual.get(2).name);
assertEquals(2, actual.get(0).ages.elementAt(0));
assertEquals(3, actual.get(0).ages.elementAt(1));
assertEquals(0, actual.get(1).ages.count());
assertEquals(6, actual.get(2).ages.elementAt(0));
```

### Reverse

```Java
List<Integer> list = new List<>(1, 2, 3);

List<Integer> actual = list.reverse().toList();

assertEquals(3, actual.get(0).intValue());
assertEquals(2, actual.get(1).intValue());
assertEquals(1, actual.get(2).intValue());
```

### Zip

```Java
List<Integer> first = new List<>(1, 2, 3);
List<String> second = new List<>("Angular", "React", "Backbone");

List<Integer> actual = first.zip(second, (x, y) -> String.format("%s %d", x, y)).toList();

assertEquals("1 Angular" , actual.get(0));
assertEquals("2 React"   , actual.get(1));
assertEquals("3 Backbone", actual.get(2));
```

### Distinct

```Java
List<Integer> list =
        new List<>(
                1, 2, 3,
                1, 2, 3, 4
        );

List<Integer> actual = list.distinct().toList();

assertEquals(1, actual.get(0).intValue());
assertEquals(2, actual.get(1).intValue());
assertEquals(3, actual.get(2).intValue());
assertEquals(4, actual.get(3).intValue());
```

### Aggregate

```Java
List<Integer> list = new List<>(1, 2, 3);

int actual = list.aggregate((sum, elem) -> sum + elem);

assertEquals(6, actual);
```

### GroupBy

```Java
List<Person> list = new List<>(
        new Person("React"   , 1),
        new Person("Angular" , 1),
        new Person("Backbone", 5)
);

Map<Integer, IEnumerable<Person>> actual = list.groupBy(x -> x.age);

assertEquals(true, actual.get(1).any(x -> x.name.equals("React")));
assertEquals(true, actual.get(1).any(x -> x.name.equals("Angular")));
assertEquals(true, actual.get(5).any(x -> x.name.equals("Backbone")));
```

### Average

```Java
List<Long> listLong = new List<>(1l, 2l, 3l, 4l);

double actualLong = listLong.averageLong(x -> x);

assertEquals(2.5d, actualLong, 0);
```

### Count

```Java
List<String> list = new List<>("Backbone", "Angular", "React");

long actual = list.longCount();
int actualNone = list.count(x -> x.equals("jquery"));

assertEquals(3, actual);
assertEquals(0, actualNone);
```

### Max

```Java
List<Double> listDouble = new List<>(1d, 2d, 3d);

double actualDouble = listDouble.max(x -> x);

assertEquals(3d, actualDouble, 0);
```

### Min

```Java
List<BigDecimal> listBigDecimal = new List<>(
        new BigDecimal(1d),
        new BigDecimal(2d),
        new BigDecimal(3d)
);

BigDecimal actualBigDecimal = listBigDecimal.min(x -> x);

assertEquals(1d, actualBigDecimal.doubleValue(), 0);
```

### Sum

```Java
List<Integer> listInt = new List<>(1, 2, 3);

int actualInt = listInt.sumInt(x -> x);

assertEquals(6, actualInt);
```

### FirstOrDefault

```Java
List<String> list = new List<>("Backbone", "Angular", "React");

String actualFirst   = list.firstOrDefault();
String actualMatch   = list.firstOrDefault(x -> x.equals("Angular"));
String actualUnMatch = list.firstOrDefault(x -> x.equals("jquery"));

assertEquals("Backbone", actualFirst);
assertEquals("Angular" , actualMatch);
assertEquals(null      , actualUnMatch);
```

### LastOrDefault

```Java
List<Integer> list = new List<>(1, 2, 3);
List<Integer> listEmpty = new List<>();

int actual = list.lastOrDefault();
Integer actualDefaultNone = listEmpty.lastOrDefault(x -> x == 0);

assertEquals(3, actual);
assertEquals(null, actualDefaultNone);
```

### SingleOrDefault

```Java
List<Integer> listMany = new List<>(1, 2, 3);
List<Integer> listEmpty = new List<>();

int actualFilter = listMany.singleOrDefault(x -> x == 3);
Integer actualUnMatch = listEmpty.singleOrDefault(x -> x == 0);

assertEquals(3, actualFilter);
assertEquals(null, actualUnMatch);
```

### DefaultIfEmpty

```Java
List<String> listEmpty = new List<>();

List<String> actualDefault = listEmpty.defaultIfEmpty("ES7").toList();

assertEquals("ES7", actualDefault.get(0));
```

### ElementAtOrDefault

```Java
List<Integer> list = new List<>(1, 2, 3);

int actual = list.elementAtOrDefault(2);
Integer actualDefault = list.elementAtOrDefault(3);

assertEquals(3, actual);
assertEquals(null, actualDefault);
```

### All

```Java
List<String> list = new List<>("Backbone", "Angular", "React");

boolean actual = list.all(x -> x.equals("Angular") || x.equals("Backbone") || x.equals("React"));
boolean actualNotFound = list.all(x -> x.equals("Angular") || x.equals("React"));

assertEquals(true, actual);
assertEquals(false, actualNotFound);
```

### Any

```Java
List<String> list = new List<>("Backbone", "Angular", "React");

boolean actual = list.any(x -> x.equals("Angular"));
boolean actualNotFound = list.any(x -> x.equals("jquery"));

assertEquals(true, actual);
assertEquals(false, actualNotFound);
```

### Empty

```Java
List<Double> actual = IEnumerable.empty(Double.class);

assertEquals(0, actual.count());
```

### Range

```Java
List<Integer> actual = IEnumerable.range(-2, 3);

assertEquals(-2, actual.get(0).intValue());
assertEquals(-1, actual.get(1).intValue());
assertEquals(0 , actual.get(2).intValue());
```

### Repeat

```Java
List<String> actual = IEnumerable.repeat(String.class, "Law of Cycles", 10);

assertEquals(10, actual.count());
assertEquals("Law of Cycles", actual.get(9));
```

### SequenceEqual

```Java
List<Integer> first = new List<>(1, 2, 3);
List<Integer> secondMatch = new List<>(1, 2, 3);
List<Integer> secondUnMatchElem = new List<>(1, 2, 4);

boolean actualMatch = first.sequenceEqual(secondMatch);
boolean actualUnMatchElm = first.sequenceEqual(secondUnMatchElem);

assertEquals(true, actualMatch);
assertEquals(false, actualUnMatchElm);
```

### Cast

```Java
List<Object> list = new List<>(1, 2, 3);

List<Integer> actual = list.cast(Integer.class).toList();

assertEquals(1, actual.get(0).intValue());
assertEquals(2, actual.get(1).intValue());
assertEquals(3, actual.get(2).intValue());
```

### OfType

```Java
List<Object> list = new List<>(1, "2", 3, "4");

List<String>  actualStr = list.ofType(String.class).toList();
List<Integer> actualInt = list.ofType(Integer.class).toList();

assertEquals("2", actualStr.get(0));
assertEquals("4", actualStr.get(1));
assertEquals(1  , actualInt.get(0).intValue());
assertEquals(3  , actualInt.get(1).intValue());
```

## 課題

　LINQの標準動作を再現出来ていない箇所があります。

* ~~遅延評価になっていない~~
 * ~~toList等で評価するように変更中~~ （変更済：2015/06/22）
* XXXorDefault
 * Integer，Long，Doubleのdefault値がnullとなってしまいます。C#はプリミティブ型でジェネリクスが可能なのでデフォルト値が0が返却できます
* GroupBy
 * 返り値がMap&lt;Key, IEnumerable&lt;TResult&gt;&gt;となっています。C#はIGroupingです。IEnumerableのMapが必要です。（検討中）
* sumXXX，averageXXX，~~minXXX~~，~~maxXXX~~
 * ジェネリクスでオーバーロードができないため，メソッド名に型を含ませています。Javaのジェネリクスでオーバーロードする議論は[stackoverflow](http://stackoverflow.com/questions/30826674/generic-method-to-perform-a-map-reduce-operation-java-8)でしました
* Listがjava.lang.Listと重複する
 * List<E>を継承して既存のListを完全に置き換えるように作っています。java.util.Listをcom.github.jlinqer.collections.Listに変更する必要があります。~~正統派Iterable<E>継承版は参考文献の"javaLinq"を参照ください~~
* ~~ThenBy/ThenByDescendingができない~~
 * ~~IOrderedEnumerableの実装が必要です。（検討中）~~ (実装済：2015/06/27)
* ~~Join~~，~~GroupJoin~~，~~Zip~~ができない 
 * （検討中）（Zip実装済：2015/07/05）（Join実装済：2015/10/26）（GroupJoin実装済：2015/10/26）

##ソースコード

　ソースコードはGitHubに公開しています。機能追加，動作障害等はPull Requestをぜひお願いします。
　
　‡GitHub, "jLinqer", https://github.com/k--kato/jLinqer
　
### バージョンについて

* 遅延評価版（本流）
 * Reuben Kuhnert "[javaLinq](https://github.com/sircodesalotOfTheRound/javaLinq)"をfolkして，Iteratorを使用してLINQの遅延評価を再現しています
 * https://github.com/k--kato/jLinqer
* 先行評価版（傍流）
 * Java 8 Streamを使用してLINQを再現しています
 * https://github.com/k--kato/jLinqer/tree/0786bb88d0788e1d1ee540af70cbad12244da9be


#参考文献

1. Microsoft Reference Source, "Enumerable.cs", http://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs
1. GitHub, "javaLinq", https://github.com/sircodesalotOfTheRound/javaLinq
1. Qiita, "LINQ to Objects と Java8-Stream API の対応表", http://qiita.com/amay077/items/9d2941283c4a5f61f302
1. stackoverflow, "Generic method to perform a map-reduce operation. (Java-8)", http://stackoverflow.com/questions/30826674/generic-method-to-perform-a-map-reduce-operation-java-8