パフォーマンスを本気で気にするLINQプログラマのためのオペーレータ表

標準ライブラリのLINQオペレータにはパフォーマンス向上のための様々なトリックが仕込まれている。
実装をざっと調べたので、その成果をここにまとめておく。

- 調査対象は.Net Core 2.。
- オーダーが同じだからと言ってパフォーマンスが同じとは限らない。
- 結構雑な調査なので信用しすぎないように。
- 項目について
  - **シグニチャの特殊化** :オーバーロードによって最適化の度合いが異なる場合、各オーバーロードを表す。
  - **ソースの特殊化** : ソースシーケンスの具象型によって最適化の度合いが異なる場合、各具象型を表す。
  - **コールスタック** : オペレータ利用による、`MoveNext`/`Current`のコールスタック深さの増加分を表す。深くなると`callvirt`呼び出しが増えるためパフォーマンスが劣化する。
  - **戻り値の特殊化型** : オペレータの戻り値が他のオペレータのパフォーマンス向上に寄与するような具象型を持つとき、その具象型を列挙する。
- オーダーの記号について
  - $n$: ソースシーケンスの要素数。複数のソースからなる場合は添え字が省略されている。
  - $k$: ソースの要素値とオペレータの引数で決まる定数。O記法ではふつうこのような値を使わないが、LINQを考える上ではなかなかに効いてくるので便宜的に導入する。

## 集計メソッド



<table cellspacing="4" border="0">
	<colgroup width="186"></colgroup>
	<colgroup span="2" width="170"></colgroup>
	<colgroup width="128"></colgroup>
	<colgroup width="851"></colgroup>
	<tr>
		<th><pre>                    </pre></th>
		<th><pre>                 </pre></th>
		<th><pre>               </pre></th>
		<th><pre>         </pre></th>
		<th><pre>                                         </pre></th>
	</tr>
	<tr>
		<th>メソッド名</th>
		<th>シグニチャの特殊化</th>
		<th>ソースの特殊化</th>
		<th>時間計算量</th>
		<th>備考</th>
	</tr>
	<tr>
		<td><code>Aggregate</code></td>
		<td><br></td>
		<td><br></td>
		<td>$O(n)$</td>
		<td><br></td>
	</tr>
	<tr>
		<td><code>Any</code></td>
		<td><br></td>
		<td><br></td>
		<td>$O(n)$</td>
		<td><br></td>
	</tr>
	<tr>
		<td><code>All</code></td>
		<td><br></td>
		<td><br></td>
		<td>$O(n)$</td>
		<td><br></td>
	</tr>
	<tr>
		<td><code>Average</code></td>
		<td><br></td>
		<td><br></td>
		<td>$O(n)$</td>
		<td><br></td>
	</tr>
	<tr>
		<td><code>Contains</code></td>
		<td><br></td>
		<td><br></td>
		<td>$O(n)$</td>
		<td><br></td>
	</tr>
	<tr>
		<td><code>Count</code></td>
		<td><br></td>
		<td><br></td>
		<td>$O(n)$</td>
		<td><br></td>
	</tr>
	<tr>
		<td rowspan=3 valign=top><code>ElementAt</code></td>
		<td valign=top><br></td>
		<td><br></td>
		<td>$O(n)$</td>
		<td><br></td>
	</tr>
	<tr>
		<td><br></td>
		<td><code>IPartition</code></td>
		<td>$O(1)$</td>
		<td><br></td>
	</tr>
	<tr>
		<td><br></td>
		<td><code>IList</code></td>
		<td>$O(1)$</td>
		<td><br></td>
	</tr>
	<tr>
		<td rowspan=3 valign=top><code>ElementAtOrDefault</code></td>
		<td valign=top><br></td>
		<td><br></td>
		<td>$O(n)$</td>
		<td><br></td>
	</tr>
	<tr>
		<td><br></td>
		<td><code>IPartition</code></td>
		<td>$O(1)$</td>
		<td><br></td>
	</tr>
	<tr>
		<td><br></td>
		<td><code>IList</code></td>
		<td>$O(1)$</td>
		<td><br></td>
	</tr>
	<tr>
		<td rowspan=3 valign=top><code>First</code></td>
		<td valign=top><br></td>
		<td><br></td>
		<td>$O(1)$</td>
		<td><br></td>
	</tr>
	<tr>
		<td><br></td>
		<td><code>IPartition</code></td>
		<td>$O(1)$</td>
		<td><br></td>
	</tr>
	<tr>
		<td><br></td>
		<td><code>IList</code></td>
		<td>$O(1)$</td>
		<td><br></td>
	</tr>
	<tr>
		<td rowspan=3 valign=top><code>FirstOrDefault</code></td>
		<td valign=top><br></td>
		<td><br></td>
		<td>$O(1)$</td>
		<td><br></td>
	</tr>
	<tr>
		<td><br></td>
		<td><code>IPartition</code></td>
		<td>$O(1)$</td>
		<td><br></td>
	</tr>
	<tr>
		<td><br></td>
		<td><code>IList</code></td>
		<td>$O(1)$</td>
		<td><br></td>
	</tr>
	<tr>
		<td rowspan=3 valign=top><code>Last</code></td>
		<td valign=top><br></td>
		<td><br></td>
		<td>$O(n)$</td>
		<td><br></td>
	</tr>
	<tr>
		<td><br></td>
		<td><code>IPartition</code></td>
		<td>$O(1)$</td>
		<td><br></td>
	</tr>
	<tr>
		<td><br></td>
		<td><code>IList</code></td>
		<td>$O(1)$</td>
		<td><br></td>
	</tr>
	<tr>
		<td rowspan=3 valign=top><code>LastOrDefault</code></td>
		<td valign=top><br></td>
		<td><br></td>
		<td>$O(n)$</td>
		<td><br></td>
	</tr>
	<tr>
		<td><br></td>
		<td><code>IPartition</code></td>
		<td>$O(1)$</td>
		<td><br></td>
	</tr>
	<tr>
		<td><br></td>
		<td><code>IList</code></td>
		<td>$O(1)$</td>
		<td><br></td>
	</tr>
	<tr>
		<td><code>Max</code></td>
		<td><br></td>
		<td><br></td>
		<td>$O(n)$</td>
		<td><br></td>
	</tr>
	<tr>
		<td><code>Min</code></td>
		<td><br></td>
		<td><br></td>
		<td>$O(n)$</td>
		<td><br></td>
	</tr>
	<tr>
		<td rowspan=3 valign=top><code>SequenceEqual</code></td>
		<td valign=top><br></td>
		<td><br></td>
		<td>$O(n)$</td>
		<td rowspan=3 valign=top>両シーケンスが<code>ICollection</code>の場合、事前に要素数チェックが入る。<br>両シーケンスがIListの場合、イテレータではなくインデクサでアクセスするためcallvirt呼び出しが減る。</td>
	</tr>
	<tr>
		<td valign=top><br></td>
		<td><code>ICollection</code></td>
		<td>$O(n)$</td>
		</tr>
	<tr>
		<td><br></td>
		<td><code>IList</code></td>
		<td>$O(n)$</td>
		</tr>
	<tr>
		<td rowspan=2 valign=top><code>Single</code></td>
		<td valign=top><br></td>
		<td><br></td>
		<td>$O(1)$</td>
		<td rowspan=2 valign=top>シーケンスが<code>IList</code>の場合、<code>Count</code>とインデクサにより処理される。</td>
	</tr>
	<tr>
		<td><br></td>
		<td><code>IList</code></td>
		<td>$O(1)$</td>
		</tr>
	<tr>
		<td><code>Sum</code></td>
		<td><br></td>
		<td><br></td>
		<td>$O(n)$</td>
		<td><br></td>
	</tr>
	<tr>
		<td rowspan=2 valign=top><code>ToArray</code></td>
		<td><br></td>
		<td><br></td>
		<td>$O(n)$</td>
		<td rowspan=4 valign=top>シーケンスが<code>IListProvider</code>の場合、各インターフェースに実装されたアルゴリズムで処理される。</td>
	</tr>
	<tr>
		<td><br></td>
		<td><code>IListProvider</code></td>
		<td>$O(n)$</td>
		</tr>
	<tr>
		<td rowspan=2 valign=top><code>ToList</code></td>
		<td><br></td>
		<td><br></td>
		<td>$O(n)$</td>
		</tr>
	<tr>
		<td><br></td>
		<td><code>IListProvider</code></td>
		<td>$O(n)$</td>
		</tr>
	<tr>
		<td rowspan=4 valign=top><code>ToDictionary</code></td>
		<td><br></td>
		<td><br></td>
		<td>$O(n)$</td>
		<td rowspan=4 valign=top>シーケンスが配列または<code>List</code>の場合、専用のアルゴリズムで処理される。<br>シーケンスが<code>ICollection</code>の場合、<code>Count</code>をもとに初期キャパシティを取得する。</td>
	</tr>
	<tr>
		<td><br></td>
		<td><code>ICollection</code></td>
		<td>$O(n)$</td>
		</tr>
	<tr>
		<td><br></td>
		<td><code>T[]</code></td>
		<td>$O(n)$</td>
		</tr>
	<tr>
		<td><br></td>
		<td><code>List</code></td>
		<td>$O(n)$</td>
		</tr>
	<tr>
		<td><code>ToHashSet</code></td>
		<td><br></td>
		<td><br></td>
		<td>$O(n)$</td>
		<td><br></td>
	</tr>
</table>
<!-- ************************************************************************** -->

## 射影メソッド

<table cellspacing="0" border="0">
	<colgroup width="144"></colgroup>
	<colgroup width="398"></colgroup>
	<colgroup width="252"></colgroup>
	<colgroup width="126"></colgroup>
	<colgroup width="128"></colgroup>
	<colgroup width="178"></colgroup>
	<colgroup width="429"></colgroup>
	<colgroup width="850"></colgroup>
        <tr>
        </tr>
		<th><pre>                </pre></th>
		<th><pre>          　　　　　　　　　　       </pre></th>
		<th><pre>                        </pre></th>
		<th><pre>              </pre></th>
		<th colspan=2><pre>             </pre></th>
		<th><pre>                        </pre></th>
		<th><pre>                                               </pre></th>
	<tr>
		<th rowspan=2 valign=middle>メソッド名</th>
		<th rowspan=2 valign=middle>シグニチャの特殊化</th>
		<th rowspan=2 valign=middle>ソースの特殊化</th>
		<th rowspan=2 valign=middle>コールスタック</th>
		<th colspan=2 valign=middle>時間計算量</th>
		<th rowspan=2 valign=middle>戻り値の特殊化型</th>
		<th rowspan=2 valign=middle>備考</th>
	</tr>
	<tr>
		<td>最初の1件</td>
		<td>全件</td>
		</tr>
	<tr>
		<td rowspan=2 valign=top><code>Append</code></td>
		<td><br></td>
		<td><br></td>
		<td sdval="1" sdnum="1041;">1</td>
		<td>$O(1)$</td>
		<td>$O(n)$</td>
		<td rowspan=2 valign=top><code>AppendPrependIterator</code>, <br /><code>Iterator</code>, <br /><code>IListProvider</code></td>
		<td rowspan=4 valign=top><code>AppendPrependIterator</code>はソースと前後のリンクリストを保持する。<br><code>AppendPrependIterator</code>への<code>Append</code>/<code>Prepend</code>はリンクリストを延長するだけなのでコールスタックが深くならない。</td>
	</tr>
	<tr>
		<td><br></td>
		<td><code>AppendPrependIterator</code></td>
		<td sdval="0" sdnum="1041;">0</td>
		<td>$O(1)$</td>
		<td>$O(n)$</td>
		</tr>
	<tr>
		<td rowspan=2 valign=top><code>Prepend</code></td>
		<td><br></td>
		<td><br></td>
		<td sdval="1" sdnum="1041;">1</td>
		<td>$O(1)$</td>
		<td>$O(n)$</td>
		<td rowspan=2 valign=top><code>AppendPrependIterator</code>, <br /><code>Iterator</code>, <br /><code>IListProvider</code></td>
		</tr>
	<tr>
		<td><br></td>
		<td><code>AppendPrependIterator</code></td>
		<td sdval="0" sdnum="1041;">0</td>
		<td>$O(1)$</td>
		<td>$O(n)$</td>
		</tr>
	<tr>
		<td valign=top><code>OfType</code></td>
		<td><br></td>
		<td><br></td>
		<td sdval="1" sdnum="1041;">1</td>
		<td>$O(1)$</td>
		<td>$O(n)$</td>
		<td><br></td>
		<td><br></td>
	</tr>
	<tr>
		<td valign=top><code>Cast</code></td>
		<td><br></td>
		<td><br></td>
		<td sdval="1" sdnum="1041;">1</td>
		<td>$O(1)$</td>
		<td>$O(n)$</td>
		<td><br></td>
		<td><br></td>
	</tr>
	<tr>
		<td rowspan=2 valign=top><code>Concat</code></td>
		<td><br></td>
		<td><br></td>
		<td sdval="1" sdnum="1041;">1</td>
		<td>$O(1)$</td>
		<td>$\sum O(n)$</td>
		<td rowspan=2 valign=top><code>ConcatIterator</code>, <br /><code>Iterator</code>, <br /><code>IListProvider</code></td>
		<td rowspan=2 valign=middle><code>ConcatIterator</code>はシーケンスのリンクを保持する。<br><code>ConcatIterator</code>への<code>Concat</code>はリンクを延長するだけなのでコールスタックが深くならない。</td>
	</tr>
	<tr>
		<td><br></td>
		<td><code>ConcatIterator</code></td>
		<td sdval="0" sdnum="1041;">0</td>
		<td>$O(1)$</td>
		<td>$\sum O(n)$</td>
		</tr>
	<tr>
		<td valign=top><code>DefaultIfEmpty</code></td>
		<td><br></td>
		<td><br></td>
		<td sdval="1" sdnum="1041;">1</td>
		<td>$O(1)$</td>
		<td>$O(n)$</td>
		<td><br></td>
		<td><br></td>
	</tr>
	<tr>
		<td valign=top><code>Distinct</code></td>
		<td><br></td>
		<td><br></td>
		<td sdval="1" sdnum="1041;">1</td>
		<td>$O(1)$</td>
		<td>$O(n)$</td>
		<td><code>Iterator</code>, <br /><code>IListProvider</code></td>
		<td><br></td>
	</tr>
	<tr>
		<td valign=top><code>Except</code></td>
		<td><br></td>
		<td><br></td>
		<td sdval="1" sdnum="1041;">1</td>
		<td>$O(n_{inner})$</td>
		<td>$\sum O(n)$</td>
		<td><br></td>
		<td><code>Set</code>に依存。</td>
	</tr>
	<tr>
		<td valign=top><code>GroupJoin</code></td>
		<td><br></td>
		<td><br></td>
		<td sdval="1" sdnum="1041;">1</td>
		<td>$O(n_{inner})$</td>
		<td>$\sum O(n)$</td>
		<td><br></td>
		<td><code>Lookup</code>に依存。</td>
	</tr>
	<tr>
		<td valign=top><code>GroupBy</code></td>
		<td><br></td>
		<td><br></td>
		<td sdval="1" sdnum="1041;">1</td>
		<td>$O(n)$</td>
		<td>$O(n)$</td>
		<td><br></td>
		<td><br></td>
	</tr>
	<tr>
		<td valign=top><code>Intersect</code></td>
		<td><br></td>
		<td><br></td>
		<td sdval="1" sdnum="1041;">1</td>
		<td>$O(n_{inner})$</td>
		<td>$\sum O(n)$</td>
		<td><br></td>
		<td><code>Set</code>に依存。</td>
	</tr>
	<tr>
		<td valign=top><code>Join</code></td>
		<td><br></td>
		<td><br></td>
		<td sdval="1" sdnum="1041;">1</td>
		<td>$O(n_{inner})$</td>
		<td>$\sum O(n)$</td>
		<td><br></td>
		<td><code>Lookup</code>に依存。</td>
	</tr>
	<tr>
		<td valign=top><code>ToLookup</code></td>
		<td><br></td>
		<td><br></td>
		<td sdval="1" sdnum="1041;">1</td>
		<td>$O(n)$</td>
		<td>$O(n)$</td>
		<td><code>IListProvider</code></td>
		<td>戻り値の実体は<code>Lookup</code>。</td>
	</tr>
	<tr>
		<td valign=top><code>OrderBy</code></td>
		<td><br></td>
		<td><br></td>
		<td sdval="1" sdnum="1041;">1</td>
		<td>$O(n)$</td>
		<td>$O(n)$</td>
		<td><code>IPartition</code>, <br /><code>IOrderedEnumerable</code></td>
		<td><br></td>
	</tr>
	<tr>
		<td valign=top><code>Reverse</code></td>
		<td><br></td>
		<td><br></td>
		<td sdval="1" sdnum="1041;">1</td>
		<td>$O(n)$</td>
		<td>$O(n)$</td>
		<td><br></td>
		<td><br></td>
	</tr>
	<tr>
		<td rowspan=7 valign=top><code>Select</code></td>
		<td rowspan=6 valign=top><sub><code>Func&lt;T, TResult&gt;</code></sub></td>
		<td><br></td>
		<td sdval="1" sdnum="1041;">1</td>
		<td>$O(1)$</td>
		<td>$O(n)$</td>
		<td><code>Iterator</code>, <br /><code>IListProvider</code></td>
		<td rowspan=7 valign=top>配列、<code>List</code>は<code>IList</code>と同じオーダーだが、一部のメソッド呼び出しにより強力な最適化が働く。<br>セレクタにインデックスをとるオーバーロードは最適化が弱い。</td>
	</tr>
	<tr>
		<td><code>Iterator</code></td>
		<td sdval="1" sdnum="1041;">1</td>
		<td>$O(1)$</td>
		<td>$O(n)$</td>
		<td><code>Iterator</code>, <br /><code>IListProvider</code></td>
		</tr>
	<tr>
		<td><code>T[]</code></td>
		<td sdval="1" sdnum="1041;">1</td>
		<td>$O(1)$</td>
		<td>$O(n)$</td>
		<td><code>Iterator</code>, <br /><code>IPartition</code></td>
		</tr>
	<tr>
		<td><code>List</code></td>
		<td sdval="1" sdnum="1041;">1</td>
		<td>$O(1)$</td>
		<td>$O(n)$</td>
		<td><code>Iterator</code>, <br /><code>IPartition</code></td>
		</tr>
	<tr>
		<td><code>IList</code></td>
		<td sdval="1" sdnum="1041;">1</td>
		<td>$O(1)$</td>
		<td>$O(n)$</td>
		<td><code>Iterator</code>, <br /><code>IPartition</code></td>
		</tr>
	<tr>
		<td><code>IPartition</code></td>
		<td sdval="1" sdnum="1041;">1</td>
		<td>$O(1)$</td>
		<td>$O(n)$</td>
		<td><code>Iterator</code>, <br /><code>IPartition</code></td>
		</tr>
	<tr>
		<td><sub><code>Func&lt;T, int, TResult&gt;</code></sub></td>
		<td><br></td>
		<td sdval="1" sdnum="1041;">1</td>
		<td>$O(1)$</td>
		<td>$O(n)$</td>
		<td><br></td>
		</tr>
	<tr>
		<td rowspan=2 valign=top><code>SelectMany</code></td>
		<td><sub><code>Func&lt;TSource, IEnumerable&lt;TResult&gt;&gt;</code></sub></td>
		<td><br></td>
		<td sdval="1" sdnum="1041;">1</td>
		<td>$O(1)$</td>
		<td>$O(n)$</td>
		<td><code>Iterator</code>, <br /><code>IListProvider</code></td>
		<td rowspan=2 valign=top>セレクタにインデックスをとるオーバーロードは最適化が弱い。</td>
	</tr>
	<tr>
		<td><sub><code>Func&lt;TSource, int, IEnumerable&lt;TResult&gt;&gt;</code></sub></td>
		<td><br></td>
		<td sdval="1" sdnum="1041;">1</td>
		<td>$O(1)$</td>
		<td>$O(n)$</td>
		<td><br></td>
		</tr>
	<tr>
		<td rowspan=3 valign=top><code>Skip</code></td>
		<td rowspan=3 valign=middle><br></td>
		<td><br></td>
		<td sdval="1" sdnum="1041;">1</td>
		<td>$O(k)$</td>
		<td>$O(n)$</td>
		<td><code>Iterator</code></td>
		<td rowspan=3 valign=top><br></td>
	</tr>
	<tr>
		<td><code>IIterator</code></td>
		<td>0 ～ 1</td>
		<td>$O(k)$</td>
		<td>$O(n)$</td>
		<td><code>Iterator</code></td>
		</tr>
	<tr>
		<td><code>IPartition</code></td>
		<td sdval="0" sdnum="1041;">0</td>
		<td>$O(1)$</td>
		<td>$O(n)$</td>
		<td><code>IPartition</code></td>
		</tr>
	<tr>
		<td valign=top><code>SkipWhile</code></td>
		<td><br></td>
		<td><br></td>
		<td sdval="1" sdnum="1041;">1</td>
		<td>$O(k)$</td>
		<td>$O(n)$</td>
		<td><br></td>
		<td><br></td>
	</tr>
	<tr>
		<td valign=top><code>SkipLast</code></td>
		<td><br></td>
		<td><br></td>
		<td sdval="1" sdnum="1041;">1</td>
		<td>$O(1)$</td>
		<td>$O(n)$</td>
		<td><br></td>
		<td><br></td>
	</tr>
	<tr>
		<td rowspan=3 valign=top><code>Take</code></td>
		<td><br></td>
		<td><br></td>
		<td sdval="1" sdnum="1041;">1</td>
		<td>$O(1)$</td>
		<td>$O(k)$</td>
		<td><code>Iterator</code>, <br /><code>IPartition</code></td>
		<td rowspan=3 valign=top><br></td>
	</tr>
	<tr>
		<td><br></td>
		<td><code>IPartition</code></td>
		<td sdval="0" sdnum="1041;">0</td>
		<td>$O(1)$</td>
		<td>$O(k)$</td>
		<td><code>Iterator</code>, <br /><code>IPartition</code></td>
		</tr>
	<tr>
		<td><br></td>
		<td><code>IList</code></td>
		<td sdval="1" sdnum="1041;">1</td>
		<td>$O(1)$</td>
		<td>$O(k)$</td>
		<td><code>Iterator</code>, <br /><code>IPartition</code></td>
		</tr>
	<tr>
		<td valign=top><code>TakeWhile</code></td>
		<td><br></td>
		<td><br></td>
		<td sdval="1" sdnum="1041;">1</td>
		<td>$O(1)$</td>
		<td>$O(k)$</td>
		<td><br></td>
		<td><br></td>
	</tr>
	<tr>
		<td rowspan=3 valign=top><code>TakeLast</code></td>
		<td><br></td>
		<td><br></td>
		<td sdval="1" sdnum="1041;">1</td>
		<td>$O(n)$</td>
		<td>$O(n)$</td>
		<td><br></td>
		<td rowspan=3 valign=top><br></td>
	</tr>
	<tr>
		<td><br></td>
		<td><code>IPartition</code></td>
		<td sdval="0" sdnum="1041;">0</td>
		<td>$O(1)$</td>
		<td>$O(k)$</td>
		<td><code>Iterator</code>, <br /><code>IPartition</code></td>
		</tr>
	<tr>
		<td><br></td>
		<td><code>IList</code></td>
		<td sdval="1" sdnum="1041;">1</td>
		<td>$O(1)$</td>
		<td>$O(k)$</td>
		<td><code>Iterator</code>, <br /><code>IPartition</code></td>
		</tr>
	<tr>
		<td rowspan=2 valign=top><code>Union</code></td>
		<td><br></td>
		<td><br></td>
		<td sdval="1" sdnum="1041;">1</td>
		<td>$O(1)$</td>
		<td>$\sum O(n)$</td>
		<td><code>UnionIterator</code>, <br /><code>Iterator</code>, <br /><code>IListProvider</code></td>
		<td rowspan=2 valign=top><code>UnionIterator</code>はシーケンスのリンクを保持し、イテレート時に重複の除外を行う。ソースが<code>UnionIterator</code>で、かつ同じ<code>comparer</code>を使っているときはリンクを追加するだけなのでコールスタックが深くならない。</td>
	</tr>
	<tr>
		<td><br></td>
		<td><code>UnionIterator</code></td>
		<td>0 ～ 1</td>
		<td>$O(1)$</td>
		<td>$\sum O(n)$</td>
		<td><code>UnionIterator</code>, <br /><code>Iterator</code>, <br /><code>IListProvider</code></td>
		</tr>
	<tr>
		<td rowspan=4 valign=top><code>Where</code></td>
		<td rowspan=3 valign=top><br></td>
		<td><br></td>
		<td sdval="1" sdnum="1041;">1</td>
		<td>$O(k)$</td>
		<td>$O(n)$</td>
		<td><code>Iterator</code>, <br /><code>IListProvider</code></td>
		<td rowspan=4 valign=top>配列、<code>List</code>は一部のメソッド呼び出しにより強力な最適化が働く。<br>セレクタにインデックスをとるオーバーロードは最適化が弱い。</td>
	</tr>
	<tr>
		<td><code>T[]</code></td>
		<td sdval="1" sdnum="1041;">1</td>
		<td>$O(k)$</td>
		<td>$O(n)$</td>
		<td><code>Iterator</code>, <br /><code>IListProvider</code></td>
		</tr>
	<tr>
		<td><code>List</code></td>
		<td sdval="1" sdnum="1041;">1</td>
		<td>$O(k)$</td>
		<td>$O(n)$</td>
		<td><code>Iterator</code>, <br /><code>IListProvider</code></td>
		</tr>
	<tr>
		<td><sub><code>Func&lt;T, int, bool&gt;</code></sub></td>
		<td><br></td>
		<td sdval="1" sdnum="1041;">1</td>
		<td>$O(k)$</td>
		<td>$O(n)$</td>
		<td><code>Iterator</code>, <br /><code>IListProvider</code></td>
		</tr>
	<tr>
		<td valign=top><code>Zip</code></td>
		<td><br></td>
		<td><br></td>
		<td sdval="1" sdnum="1041;">1</td>
		<td>$O(1)$</td>
		<td>$\min(O(n))$</td>
		<td><br></td>
		<td><br></td>
	</tr>
</table>

## 最適化に用いられる型

### `Iterator`クラス

LINQオペレータで広く利用される抽象クラス。`IEnumerable`, `IEnumerator`を実装している。
ステートとスレッドIDを管理しており、自身を生成したスレッド内かつ唯一の利用なら`GetEnumerator`が自身を返すことでオブジェクト数を増やさないようになっていたりする。
ステートフィールドの解釈は子クラス側に委ねられており、OOP的にはあまりよろしくない設計となっているが、パフォーマンスを出すためには仕方ないことなのだろうか。

### `IListProvider`インターフェース

`ToArray`, `ToList`の専用実装を用意できるようにするインターフェース。

### `IPartition`インターフェース

ランダムアクセス可能なシーケンスであることを表すインターフェース。参照型向けの`Span`みたいなもの。

### `Lookup`クラス

`ILookup`の実装。`ToLookup`や`GroupBy`などで用いられる、グループキーとシーケンスの軽量な辞書。

### `Set`クラス

軽量なハッシュセットクラス。`System.Collections.Generic.HashSet`はコアライブラリで使うにはリッチすぎるということだろうか。

## まとめ

- 同じオペレータを連続適用すると効率が良い(ことがそこそこある)
- `Select`/`Where`はインデックス込みで評価できるオーバーロードを使うと最適化が利かなくなったりする。

## 最後に

**正直こういうの考える必要があるほどパフォーマンスが大事なら素直に`foreach`で書いた方がいいと思う。**

<font color="lightgray">QiitaでHTML属性使えなくなってテーブルきれいに書くのとてもつらいんだけど誰かいい方法教えて</font>