プログラムdeタマゴ

nodamushiの著作物は、文章、画像、プログラムにかかわらず全てUnlicenseです

C#ぼっち勉強会 リスト

 Javaを離れて(といいつつ毎週何回かはEclipseが立ち上がる)、思うのだが

.Netのドキュメントが読みづらいことこの上ない。

 なんで、戻り値の型とか、一々関数の説明見に行かないと見えないんだ?  ローカル変数の型はvarで省略出来るとか関係ない。  ドキュメントを読んでるとき、そこからリンクを辿って色々クラスを見ることが結構重要だ。  だが、.Netでそれをするのは非常にしんどい。サイトが開くのが遅い上に、何度もジャンプしないといけないとか勘弁してくれ。

 JavaDocに慣れてるからじゃなくて、圧倒的にJavaDocの方が優れているのは間違いないと思う。  Doxygenで書いた方がいいんじゃないかってレベル。

 とりあえず、ブログじゃねぇんだから3カラムは耐えられない。一先ずは、ユーザースタイルシートで誤魔化したけど。

 というわけで、今日はC#のリスト

C#のリスト

 C#でリストを使うには以下を宣言しておく。

using System.Collections.Generic;

 ソースコードはここ.Net  Frameworkはソースコード探しやすいのに、coreのはないのかなぁ。

IList

 JavaでいうList

JavaのList C#のIList
add(value) Add(value)
clear() Clear()
contains(value) Contains(value)
containsAll(collection)
get(index) list[index]
indexOf(value) IndexOf(value)
insert(index,value) Insert(index,value)
isEmpty()
remove(index) RemoveAt(index)
remove(value) Remove(value)
size() Count
subList(from,to)
set(index,value) list[index]=value
toArray()

 toArrayとかはList<T>とかで実装されてるけど、IListとしてはないっぽい。

 というか、subListがないのね。Spanが使えるかと思ったけど、使えないみたい。

 一応、GetRangeというメソッドがsubListに似た動作をするけど、コピーを作ってしまう

 conatinsAllは実装されていないが、Linqを使えばいけるっぽい

// using System.Linqが必要
var list1 = new List<int>{1,2,3,4,5,6,7,8,9};
var list2 = new List<int>{1,3,5,7,9};
var list3 = new List<int>{1,3,-5,7,9};
Console.WriteLine($"list1 contains list2 ={  !list2.Except(list1).Any()  }");//true
Console.WriteLine($"list1 contains list3 ={  !list3.Except(list1).Any()  }");//false

 相手の要素から自分の要素を全部削除してみて、何も残ってなければcontainsAll………ってメンドウクサ

List<T>

 ソースコードList.cs

 JavaでいうArrayList<T>のこと。

 C++のvectorライクな初期化方法は出来ず、非常になんか、中途半端なコレクション初期化子で初期化する。

// List<int> list{1,2,3}; これ駄目らしい
var list = new List<int>{1,2,3,4};

 ildasm.exeでデアセンブルしたところ、new List<int>(new int[]{1,2,3,4})ではなく、以下の様になる。

var list = new List<int>();
list.Add(1);  list.Add(2);
list.Add(3);  list.Add(4);

 う~ん。まぁ、きっと最適化されて大したことはないのでしょう。

 Javaと違い、Sort関数、Reverse関数などが実装されている。(Java8からはsortは追加されたけど)

LinkedList<T>

 JavaのLinkedList。ソースコードはこれなんかな?

 Addを実装してない為、コレクション初期化子を使うとエラーになる。  ………え、Addがないってどういうこと。AddLastをAddにしちゃあかんかったん?うーん、わからん。

//var list = new LinkedList<int>{1,2,3}; エラー
var list = new LinkedList<int>(new int[]{1,2,3});

 JavaのLinkedListと違い、いわゆるPairデータ(LinkedListNode)を直に弄る。  JavaよりLispに近いデータ操作方法で、抽象化が低い。list.CDDDARとか書かせて欲しい。

var llist = new LinkedList<string>(new string[]{"a","b","c","d"});
var third = llist.First.Next.Next;
Console.WriteLine($"3rd = {third.Value}");

 しかし、なんでこんなインターフェースなんかなぁ。Javaの方が優秀だと思うんだよなぁ。  リンクリスト形式のデータ構造を作る時に便利かなとか思ったけど、ぶっちゃけ、自前のクラス内でリンク構造作った方が楽なんだよな。

 特に実装しなくてもforeachやLinqを使う場合に便利だったりとか、そんな感じ?う~ん…

StackとQueue

 後入れ先出し(LIFO)と先入れ先出し(FIFO)のデータ構造。ソースコードはStackはこれQueueはこれかな?

 一応LinkedListでもStackとQueueは作れるけど、これらのクラスは配列でデータを保持しているから、効率が多分良い。

 当然、Add関数はないのでコレクション初期化子は使えな………って、いい加減にしろよ。C++を見習え!

 Stack

Java Stack C# Stack
size() Count
pop() Pop()
push(item) Push(item)

 Queue

Java Queue C# Queue
size() Count
element() Peek()
peek() 一応tryPeek(out result)がそれっぽい
poll() Dequeue()
add(item) Enqueue(item)

 tryPeekめんどい。

Immutable

 ImmutableなリストはSystem.Collections.Immutableで提供されている。ソースコードはこれかな?

 利用するにはnugetでインストールする必要がある。(以下のコマンド)

dotnet add package System.Collections.Immutable

 リストとしてはImmutableArray構造体とImmutableListクラスがある。

 どちらもコンストラクタはないので、Createを使うか、Builderを使う。

var array = ImmutableArray.Create(1,2,3,4,5);

var builder = ImmutableList.CreateBuilder<string>();
builder.Add("a");builder.Add("b");builder.Add("c"); 
var list =builder.ToImmutableList();                   

 実装を読むと、ImmutableArrayは中身は配列で、ImmutableListは2分木構造になっている模様。なので、ImmutableArrayはアクセスがO(1)、ImmutableListはO(logn)。

 基本的にはImmutableArrayで良いかなって思う。 Immutableなリストって基本的に最初に作ったらアクセスしかないし。foreachとかはメモ化して高速化してるみたいだけど、それでもやっぱりImmutableArrayの方が単純で速いハズ。

 Addなどの操作メソッドに関しては、ImmutableArrayもImmutableListも変更の代わりに、新たなリストを返す。この辺はJavaのPaguroと同じだね。でも、やっぱりサブリストはない。不変なんだから、サブリストあっても良いんじゃないんですかねぇ。

 ところで、注意点はImmutableArrayって 構造体 だよね。

 つまり、値型なので、引数として受け取る場合にはinで受け取らないと無駄なコピーが出来る。 でも、ImmutableArrayの中身って配列(参照型)しかないっぽいから、実はコピーの方が早かったりするんだろうか?その辺りどうなんだろう?

 ImmutableStackとImmutableQueueについては、中身は配列だった。後はだいたい同様。

 次はDictionary(Map)とSetかな。