2
3

More than 1 year has passed since last update.

【C#/List】リスト処理のチートシート

Last updated at Posted at 2021-12-17

はじめに

C#のListクラスの使い方について簡潔にまとめております。

何か発見があり次第、今後も追加して行きます。

環境

当ページでは以下の環境で動作確認をしました。

  • Windows 11
  • csc.exe(Windows標準搭載のC#コンパイラ)
    • Visual Studio 2022でも大丈夫です。

配列とListの使い分け

配列は固定サイズ、Listは動的サイズ(後からサイズを変更できる)というのが一般的な理解だと思います。
ですがこれらの使い分けはどんな場合なのか?ってことについてはあまり記されてないかと思います。

そこで投稿者の独断と偏見でこれらの使い分けを一通り記してみますので、ぜひ参考にしてみてくださいね。

〇:おススメ
△:おススメはしない(腕に自信があるなら問題ないかと...)
×:絶対やめとけ

項目 配列 List
予めサイズが仕様書で決まってる?
後からデータの追加がある? ×
ハードウェアのメモリの制限がある?
パフォーマンス(処理速度)を求める?
機械学習やディープラーニング?
ゲーム開発?

これだけだとちょっとピンとこないので、もうちょっとだけ詳しく使いどころを書きます。

  • 配列の使いどころ

    • データサイズが仕様書でガチガチに決められている
    • プログラムの処理速度をできるだけ上げたい
    • マイコン、Arduino、Raspberry Piといった低スペックなハードウェア環境で動かさなければならない
    • 【勉強として】メモリの管理を自分で考えてコーディングしたい
  • Listの使いどころ

    • メモリが16GB以上ある高性能なハードウェアで動かせれる
    • 後で仕様変更があっても柔軟に対応できるようにしたい
    • 機械学習やディープラーニングといった、メモリを湯水のように使いまくるプログラム
    • 最近のC#ではML.netPandas.netTensorFlow.netといった機械学習向けのライブラリが充実してきました。

ゲーム開発では、ジャンルに応じて負荷があまりにも違うので何とも言えません。 例えばシューティング系や無双系といったNPCが無尽蔵にポコポコ出てくるようなゲームではListを使用すべきですし、配列だと難しい、あるいは不可能だと思います(実際に作ったことがないので憶測で言ってます)。 ですがRPGのような「今映っているフィールド上での敵数の上限が10匹くらいまで」とか、マインクラフトのように「200匹まで自然にMobをスポーンさせる」といったようにサイズを明確に決めていれば配列の方がSwitchやスマホといった低スペックな環境でも動かせれるようにできますね。

List と ArrayList

当ページではArrayListの説明は省き、Listの説明だけにします。

C#の動的配列の型にはListとArrayListの2つがあります。
これらの違いを簡単に記すと以下の表のとおりになります。

項目 List ArrayList(公式:非推奨)
データの型 すべて統一 バラバラでもOK
データの読み出し 特に意識しなくてよい 型に合わせてキャストが必要

図で表すとこんな感じですね。

000.png

ArrayListはPythonのリストのように型を意識せずに使用できる面、非常に便利そうですが、Microsoft公式では以下のように使うことをおススメはしていません。

ArrayListには以下のデメリットがあるようです。 * パフォーマンスの低下(重くなる) * ソート(並べ替え)の処理は保証されず、ヘンテコな並び替えになる場合もある * ArrayListに要素を追加するときにメモリ再割り当てが発生し、必要以上のメモリ容量が要求される。 公式は非推奨としていますので、当ページではArrayListの説明は省略します。

まぁ一言でいえば「性能が悪くなる」ということです。

Listの初期化

変数宣言と同時に値・データを挿入する方法(初期化)についてです。

【初期化】一次元のList

一次元Listの初期化するサンプルプログラムになります。

using System;
using System.Collections.Generic;   // Listクラスを使用するのに必要

class Program {
    static void Main(string[] args) {
        List<int>    intL = new List<int>() {0, 1, 2, 3, 4};
        List<string> strL = new List<string>() {"Zero", "One", "Two", "Three", "Four"};

        // for 文で各要素を取得
        for (int i = 0; i < intL.Count; i++) {
            Console.WriteLine("intL[{0}]: {1}", i, intL[i]);
        }

        Console.WriteLine("----- ----- ----- -----");

        // foreach 文で各要素を取得
        foreach (string str in strL) {
            Console.WriteLine("strL[{0}]: {1}", strL.IndexOf(str), str);
        }
    }
}

コンパイルと実行結果

* csc.exeによるコンパイル
csc.exe /target:exe Program.cs
  • 実行結果
> .\Program.exe
intL[0]: 0
intL[1]: 1
intL[2]: 2
intL[3]: 3
intL[4]: 4
----- ----- ----- -----
strL[0]: Zero
strL[1]: One
strL[2]: Two
strL[3]: Three
strL[4]: Four

Pythonを知っている人であれば、Pythonの一次元リストと比較すると分かりやすいかと思います。

Pythonのリストと比べてみる
  • C# での一次元リストの初期化
List<int>    intL = new List<int>() {0, 1, 2, 3, 4};
List<string> strL = new List<string>() {"Zero", "One", "Two", "Three", "Four"};
  • Python での一次元リストの初期化
intL = [0, 1, 2, 3, 4]
strL = ["Zero", "One", "Two", "Three", "Four"]

【初期化】二次元のList

二次元Listの初期化するサンプルプログラムになります。

using System;
using System.Collections.Generic;   // Listクラスを使用するのに必要

class Program {
    static void Main(string[] args) {
        List<List<int>> intLL = new List<List<int>>() {
            new List<int>() {1, 2, 3},
            new List<int>() {4, 5, 6},
            new List<int>() {7, 8, 9}
        };  // ここのセミコロンを忘れずに

        List<List<string>> strLL = new List<List<string>>() {
            new List<string>() {"One", "Two", "Three"},
            new List<string>() {"Four", "Five", "Six"},
            new List<string>() {"Seven", "Eight", "Nine"}
        };  // ここのセミコロンを忘れずに

        // for 文で各要素を取得
        for (int i = 0; i < intLL.Count; i++) {
            for (int j = 0; j < intLL[i].Count; j++) {
                Console.WriteLine("intLL[{0}][{1}]: {2}", i, j, intLL[i][j]);
            }
        }

        Console.WriteLine("----- ----- ----- ----- -----");

        // foreach 文で各要素を取得
        foreach (var list in strLL) {
            foreach (var str in list) {
                Console.WriteLine("strLL[{0}][{1}]: {2}", strLL.IndexOf(list), list.IndexOf(str), str);
            }
        }
    }
}

コンパイルと実行結果

* csc.exeによるコンパイル
csc.exe /target:exe Program.cs
  • 実行結果
> .\Program.exe
intLL[0][0]: 1
intLL[0][1]: 2
intLL[0][2]: 3
intLL[1][0]: 4
intLL[1][1]: 5
intLL[1][2]: 6
intLL[2][0]: 7
intLL[2][1]: 8
intLL[2][2]: 9
----- ----- ----- ----- -----
strLL[0][0]: One
strLL[0][1]: Two
strLL[0][2]: Three
strLL[1][0]: Four
strLL[1][1]: Five
strLL[1][2]: Six
strLL[2][0]: Seven
strLL[2][1]: Eight
strLL[2][2]: Nine

Pythonを知っている人であれば、Pythonの二次元リストと比較すると分かりやすいかと思います。

Pythonのリストと比べてみる

* C# での二次元リストの初期化
List<List<int>> intLL = new List<List<int>>() {
    new List<int>() {1, 2, 3},
    new List<int>() {4, 5, 6},
    new List<int>() {7, 8, 9}
};  // ここのセミコロンを忘れずに

List<List<string>> strLL = new List<List<string>>() {
    new List<string>() {"One", "Two", "Three"},
    new List<string>() {"Four", "Five", "Six"},
    new List<string>() {"Seven", "Eight", "Nine"}
};  // ここのセミコロンを忘れずに
  • Python での二次元リストの初期化
intLL = [[1, 2, 3],
         [4, 5, 6],
         [7, 8, 9]]

strLL = [["One", "Two", "Three"],
         ["Four", "Five", "Six"],
         ["Seven", "Eight", "Nine"]]

【初期化】三次元のList

三次元Listの初期化するサンプルプログラムになります。

using System;
using System.Collections.Generic;   // Listクラスを使用するのに必要

class Program {
    static void Main(string[] args) {
        List<List<List<int>>> intLLL = new List<List<List<int>>>() {
            new List<List<int>>() {
                new List<int>() {01, 02, 03},
                new List<int>() {04, 05, 06},
                new List<int>() {07, 08, 09}
            },
            new List<List<int>>() {
                new List<int>() {11, 12, 13},
                new List<int>() {14, 15, 16},
                new List<int>() {17, 18, 19}
            },
            new List<List<int>>() {
                new List<int>() {21, 22, 23},
                new List<int>() {24, 25, 26},
                new List<int>() {27, 28, 29}
            }
        };  // ここのセミコロンを忘れずに

        List<List<List<string>>> strLLL = new List<List<List<string>>>() {
            new List<List<string>>() {
                new List<string>() {"One", "Two", "Three"},
                new List<string>() {"Four", "Five", "Six"},
                new List<string>() {"Seven", "Eight", "Nine"}
            },
            new List<List<string>>() {
                new List<string>() {"Eleven", "Twelve", "Thirteen"},
                new List<string>() {"Fourteen", "Fifteen", "Sixteen"},
                new List<string>() {"Seventeen", "Eighteen", "Nineteen"}
            },
            new List<List<string>>() {
                new List<string>() {"Twenty-One", "Twenty-Two", "Twenty-Three"},
                new List<string>() {"Twenty-Four", "Twenty-Five", "Twenty-Six"},
                new List<string>() {"Twenty-Seven", "Twenty-Eight", "Twenty-Nine"}
            }
        };  // ここのセミコロンを忘れずに

        // for 文で各要素を取得
        for (int i = 0; i < intLLL.Count; i++) {
            for (int j = 0; j < intLLL[i].Count; j++) {
                for (int k = 0; k < intLLL[i][j].Count; k++) {
                    Console.WriteLine("intLLL[{0}][{1}][{2}]: {3}", i, j, k, intLLL[i][j][k]);
                }
            }
        }

        Console.WriteLine("----- ----- ----- ----- -----");

        // foreach 文で各要素を取得
        foreach (var LL in strLLL) {
            foreach (var list in LL) {
                foreach (var str in list) {
                    Console.WriteLine("strLLL[{0}][{1}][{2}]: {3}", strLLL.IndexOf(LL), LL.IndexOf(list), list.IndexOf(str), str);
                }
            }
        }
    }
}

コンパイルと実行結果

* csc.exeによるコンパイル
csc.exe /target:exe Program.cs
  • 実行結果
> .\Program.exe
intLLL[0][0][0]: 1
intLLL[0][0][1]: 2
intLLL[0][0][2]: 3
intLLL[0][1][0]: 4
intLLL[0][1][1]: 5
intLLL[0][1][2]: 6
intLLL[0][2][0]: 7
intLLL[0][2][1]: 8
intLLL[0][2][2]: 9
intLLL[1][0][0]: 11
intLLL[1][0][1]: 12
intLLL[1][0][2]: 13
intLLL[1][1][0]: 14
intLLL[1][1][1]: 15
intLLL[1][1][2]: 16
intLLL[1][2][0]: 17
intLLL[1][2][1]: 18
intLLL[1][2][2]: 19
intLLL[2][0][0]: 21
intLLL[2][0][1]: 22
intLLL[2][0][2]: 23
intLLL[2][1][0]: 24
intLLL[2][1][1]: 25
intLLL[2][1][2]: 26
intLLL[2][2][0]: 27
intLLL[2][2][1]: 28
intLLL[2][2][2]: 29
----- ----- ----- ----- -----
strLLL[0][0][0]: One
strLLL[0][0][1]: Two
strLLL[0][0][2]: Three
strLLL[0][1][0]: Four
strLLL[0][1][1]: Five
strLLL[0][1][2]: Six
strLLL[0][2][0]: Seven
strLLL[0][2][1]: Eight
strLLL[0][2][2]: Nine
strLLL[1][0][0]: Eleven
strLLL[1][0][1]: Twelve
strLLL[1][0][2]: Thirteen
strLLL[1][1][0]: Fourteen
strLLL[1][1][1]: Fifteen
strLLL[1][1][2]: Sixteen
strLLL[1][2][0]: Seventeen
strLLL[1][2][1]: Eighteen
strLLL[1][2][2]: Nineteen
strLLL[2][0][0]: Twenty-One
strLLL[2][0][1]: Twenty-Two
strLLL[2][0][2]: Twenty-Three
strLLL[2][1][0]: Twenty-Four
strLLL[2][1][1]: Twenty-Five
strLLL[2][1][2]: Twenty-Six
strLLL[2][2][0]: Twenty-Seven
strLLL[2][2][1]: Twenty-Eight
strLLL[2][2][2]: Twenty-Nine

Pythonを知っている人であれば、Pythonの三次元リストと比較すると分かりやすいかと思います。

Pythonのリストと比べてみる

* C# での三次元リストの初期化
List<List<List<int>>> intLLL = new List<List<List<int>>>() {
    new List<List<int>>() {
        new List<int>() {01, 02, 03},
        new List<int>() {04, 05, 06},
        new List<int>() {07, 08, 09}
    },
    new List<List<int>>() {
        new List<int>() {11, 12, 13},
        new List<int>() {14, 15, 16},
        new List<int>() {17, 18, 19}
    },
    new List<List<int>>() {
        new List<int>() {21, 22, 23},
        new List<int>() {24, 25, 26},
        new List<int>() {27, 28, 29}
    }
};  // ここのセミコロンを忘れずに

List<List<List<string>>> strLLL = new List<List<List<string>>>() {
    new List<List<string>>() {
        new List<string>() {"One", "Two", "Three"},
        new List<string>() {"Four", "Five", "Six"},
        new List<string>() {"Seven", "Eight", "Nine"}
    },
    new List<List<string>>() {
        new List<string>() {"Eleven", "Twelve", "Thirteen"},
        new List<string>() {"Fourteen", "Fifteen", "Sixteen"},
        new List<string>() {"Seventeen", "Eighteen", "Nineteen"}
    },
    new List<List<string>>() {
        new List<string>() {"Twenty-One", "Twenty-Two", "Twenty-Three"},
        new List<string>() {"Twenty-Four", "Twenty-Five", "Twenty-Six"},
        new List<string>() {"Twenty-Seven", "Twenty-Eight", "Twenty-Nine"}
    }
};  // ここのセミコロンを忘れずに
  • Python での三次元リストの初期化
intLLL = [[[1, 2, 3],
           [4, 5, 6],
           [7, 8, 9]],
          [[11, 12, 13],
           [14, 15, 16],
           [17, 18, 19]],
          [[21, 22, 23],
           [24, 25, 26],
           [27, 28, 29]]]

strLLL = [[["One", "Two", "Three"],
           ["Four", "Five", "Six"],
           ["Seven", "Eight", "Nine"]],

          [["Eleven", "Twelve", "Thirteen"],
           ["Fourteen", "Fifteen", "Sixteen"],
           ["Seventeen", "Eighteen", "Nineteen"]],

          [["Twenty-One", "Twenty-Two", "Twenty-Three"],
           ["Twenty-Four", "Twenty-Five", "Twenty-Six"],
           ["Twenty-Seven", "Twenty-Eight", "Twenty-Nine"]]]

リストの次元が増えれば増えるほど、Pythonの方が圧倒的にコーディングしやすいですね。
やはりPythonは人間に優しかった。

Listに新要素の追加

List型の変数に値・データを追加する方法についてです。

【Add】要素を追加

List変数に要素を追加するのにAddメソッドを使用します。
Addメソッドの書式は次の通り。

using System.Collections.Generic;

// T: 型(intやstringなど)
// item: 追加する要素(型は T と同じじゃないとエラー)
List<T>.Add(item)

一つ注意なのですが、Addメソッドで要素を追加すると、元々のList変数の一番最後に追加されます。
図で表すとこんな感じ。

001.png

指定した場所に新要素を追加したい場合は`Insert`メソッドを使用すればOK。 `Insert`は後で出てきます。

【要素の追加】一次元のList

using System;
using System.Collections.Generic;   // Listクラスを使用するのに必要

class Program {
    static void Main(string[] args) {
        List<string> strL = new List<string>() {"Apple", "Meron", "Orange", "Pine"};

        foreach (var str in strL) {
            Console.WriteLine("strL[{0}]: {1}", strL.IndexOf(str), str);
        }

        Console.WriteLine("----- ----- ----- ----- -----");

        // 末尾に追加
        strL.Add("Peach");

        foreach (var str in strL) {
            Console.WriteLine("strL[{0}]: {1}", strL.IndexOf(str), str);
        }
    }
}

コンパイルと実行結果

* csc.exeによるコンパイル
csc.exe /target:exe Program.cs
  • 実行結果
> .\Program.exe
strL[0]: Apple
strL[1]: Meron
strL[2]: Orange
strL[3]: Pine
----- ----- ----- ----- -----
strL[0]: Apple
strL[1]: Meron
strL[2]: Orange
strL[3]: Pine
strL[4]: Peach

Pythonを知っている人であれば、Pythonの一次元リストと比較すると分かりやすいかと思います。

Pythonのリストと比べてみる

* C# での一次元リストの要素追加
strL.Add("Peach");
  • Python での一次元リストの要素追加
strL.append("Peach")

【要素の追加】二次元のList

using System;
using System.Collections.Generic;   // Listクラスを使用するのに必要

class Program {
    static void Main(string[] args) {
        List<List<string>> strLL = new List<List<string>>() {
            new List<string>() {"Apple", "Meron",     "Orange", "Pine"},
            new List<string>() {"Japan", "America",   "Chaina", "Canada"},
            new List<string>() {"Math",  "Chemistry", "Histry", "Japanese"},
            new List<string>() {"Wine",  "Beer",      "Sake",   "Whiskey"}
        };

        foreach (var strL in strLL) {
            foreach (var str in strL) {
                Console.Write("strLL[{0}][{1}]: {2}\t", strLL.IndexOf(strL), strL.IndexOf(str), str);
            }
            Console.WriteLine("");
        }

        Console.WriteLine("----- ----- ----- ----- -----");

        // 末尾に追加(一次元Listであることに注意)
        strLL.Add(new List<string>() {"Red", "Yellow", "Blue", "Green"});

        foreach (var strL in strLL) {
            foreach (var str in strL) {
                Console.Write("strLL[{0}][{1}]: {2}\t", strLL.IndexOf(strL), strL.IndexOf(str), str);
            }
            Console.WriteLine("");
        }
    }
}

コンパイルと実行結果

* csc.exeによるコンパイル
csc.exe /target:exe Program.cs
  • 実行結果
> .\Program.exe
strLL[0][0]: Apple      strLL[0][1]: Meron      strLL[0][2]: Orange     strLL[0][3]: Pine
strLL[1][0]: Japan      strLL[1][1]: America    strLL[1][2]: Chaina     strLL[1][3]: Canada
strLL[2][0]: Math       strLL[2][1]: Chemistry  strLL[2][2]: Histry     strLL[2][3]: Japanese
strLL[3][0]: Wine       strLL[3][1]: Beer       strLL[3][2]: Sake       strLL[3][3]: Whiskey
----- ----- ----- ----- -----
strLL[0][0]: Apple      strLL[0][1]: Meron      strLL[0][2]: Orange     strLL[0][3]: Pine
strLL[1][0]: Japan      strLL[1][1]: America    strLL[1][2]: Chaina     strLL[1][3]: Canada
strLL[2][0]: Math       strLL[2][1]: Chemistry  strLL[2][2]: Histry     strLL[2][3]: Japanese
strLL[3][0]: Wine       strLL[3][1]: Beer       strLL[3][2]: Sake       strLL[3][3]: Whiskey
strLL[4][0]: Red        strLL[4][1]: Yellow     strLL[4][2]: Blue       strLL[4][3]: Green

Pythonを知っている人であれば、Pythonの一次元リストと比較すると分かりやすいかと思います。

Pythonのリストと比べてみる

* C# での二次元リストの要素追加
strLL.Add(new List<string>() {"Red", "Yellow", "Blue", "Green"});
  • Python での二次元リストの要素追加
strLL.append(["Red", "Yellow", "Blue", "Green"])

【要素の追加】三次元のList

using System;
using System.Collections.Generic;   // Listクラスを使用するのに必要

class Program {
    static void Main(string[] args) {
        // Listならサイズが違ってても格納できる
        List<List<List<int>>> strLLL = new List<List<List<int>>>() {
            new List<List<int>>() {
                new List<int>() {0},
                new List<int>() {0, 1},
                new List<int>() {0, 1, 2}
            },
            new List<List<int>>() {
                new List<int>() {0, 1, 2, 3}
            }
        };

        foreach (var strLL in strLLL) {
            foreach (var strL in strLL) {
                foreach (var str in strL) {
                    Console.Write("strLLL[{0}][{1}][{2}]: {3}\t", strLLL.IndexOf(strLL), strLL.IndexOf(strL), strL.IndexOf(str), str);
                }
                Console.WriteLine();
            }
            Console.WriteLine();
        }

        Console.WriteLine("----- ----- ----- ----- -----");

        // 末尾に追加(二次元Listであることに注意)
        strLLL.Add(new List<List<int>>() {
            new List<int>() {0},
            new List<int>() {0, 1},
            new List<int>() {0, 1, 2},
            new List<int>() {0, 1, 2, 3},
            new List<int>() {0, 1, 2, 3, 4},
            new List<int>() {0, 1, 2, 3, 4, 5},
            new List<int>() {0, 1, 2, 3, 4, 5, 6}
        });

        foreach (var strLL in strLLL) {
            foreach (var strL in strLL) {
                foreach (var str in strL) {
                    Console.Write("strLLL[{0}][{1}][{2}]: {3}\t", strLLL.IndexOf(strLL), strLL.IndexOf(strL), strL.IndexOf(str), str);
                }
                Console.WriteLine();
            }
            Console.WriteLine();
        }
    }
}

コンパイルと実行結果

* csc.exeによるコンパイル
csc.exe /target:exe Program.cs
  • 実行結果
> .\Program.exe
strLLL[0][0][0]: 0
strLLL[0][1][0]: 0      strLLL[0][1][1]: 1
strLLL[0][2][0]: 0      strLLL[0][2][1]: 1      strLLL[0][2][2]: 2

strLLL[1][0][0]: 0      strLLL[1][0][1]: 1      strLLL[1][0][2]: 2      strLLL[1][0][3]: 3

----- ----- ----- ----- -----
strLLL[0][0][0]: 0
strLLL[0][1][0]: 0      strLLL[0][1][1]: 1
strLLL[0][2][0]: 0      strLLL[0][2][1]: 1      strLLL[0][2][2]: 2

strLLL[1][0][0]: 0      strLLL[1][0][1]: 1      strLLL[1][0][2]: 2      strLLL[1][0][3]: 3

strLLL[2][0][0]: 0
strLLL[2][1][0]: 0      strLLL[2][1][1]: 1
strLLL[2][2][0]: 0      strLLL[2][2][1]: 1      strLLL[2][2][2]: 2
strLLL[2][3][0]: 0      strLLL[2][3][1]: 1      strLLL[2][3][2]: 2      strLLL[2][3][3]: 3
strLLL[2][4][0]: 0      strLLL[2][4][1]: 1      strLLL[2][4][2]: 2      strLLL[2][4][3]: 3      strLLL[2][4][4]: 4
strLLL[2][5][0]: 0      strLLL[2][5][1]: 1      strLLL[2][5][2]: 2      strLLL[2][5][3]: 3      strLLL[2][5][4]: 4     strLLL[2][5][5]: 5
strLLL[2][6][0]: 0      strLLL[2][6][1]: 1      strLLL[2][6][2]: 2      strLLL[2][6][3]: 3      strLLL[2][6][4]: 4     strLLL[2][6][5]: 5       strLLL[2][6][6]: 6

Pythonを知っている人であれば、Pythonの一次元リストと比較すると分かりやすいかと思います。

Pythonのリストと比べてみる

* C# での三次元リストの要素追加
strLLL.Add(new List<List<int>>() {
    new List<int>() {0},
    new List<int>() {0, 1},
    new List<int>() {0, 1, 2},
    new List<int>() {0, 1, 2, 3},
    new List<int>() {0, 1, 2, 3, 4},
    new List<int>() {0, 1, 2, 3, 4, 5},
    new List<int>() {0, 1, 2, 3, 4, 5, 6}
});
  • Python での三次元リストの要素追加
strLLL.append([[0],
               [0, 1],
               [0, 1, 2],
               [0, 1, 2, 3],
               [0, 1, 2, 3, 4],
               [0, 1, 2, 3, 4, 5],
               [0, 1, 2, 3, 4, 5, 6]])

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3