【C++】コンテナクラス

## 動的配列
**動的配列**とは
- 動的配列(C++の配列、**実行中にサイズの変更が可能**)
⇔**固定長配列**(C言語の配列、実行開始時からサイズの変更が不可能)
- 配列と同様の操作が可能。また、**データの追加、削除が可能**
- 配列と同様にデータは先頭から末尾まで順にメモリに格納されており、
連続したデータである為、**末尾(最後の次)以外へのデータの追加、削除は負荷が大きい**

**用途 : オブジェクトを管理するリストなど**

### vector(C++)
```C++:Example.cpp
#include <iostream>
#include <vector>
#include <format>

int main(void)
{
	// 宣言・定義
	// std::vector<型> 変数名;
	std::vector<int> iVector = { 1, 2, 3, 4, 5 };

	std::cout << std::format("確保しているメモリの容量 : {}", iVector.capacity()) << std::endl;
	std::cout << std::format("格納している要素の数 : {}", iVector.size()) << std::endl;

	// 要素の追加(末尾)
	// std::vector.push_back(値);
	iVector.push_back(6);

	std::cout << std::format("確保しているメモリの容量 : {}", iVector.capacity()) << std::endl;
	std::cout << std::format("格納している要素の数 : {}", iVector.size()) << std::endl;
	
	// 要素の追加(任意)
	// std::vector.insert(iterator.position, 値);
	iVector.insert(iVector.begin(), -1);
	iVector.insert(iVector.end(), 7);

	std::cout << std::format("確保しているメモリの容量 : {}", iVector.capacity()) << std::endl;
	std::cout << std::format("格納している要素の数 : {}", iVector.size()) << std::endl;

	// (※)イテレータの定義
	// std::vector<型>::iterator 変数名;
	std::vector<int>::iterator itr = iVector.begin();
	itr += 4;
	iVector.insert(itr, 100);
    
	std::cout << std::format("確保しているメモリの容量 : {}", iVector.capacity()) << std::endl;
	std::cout << std::format("格納している要素の数 : {}", iVector.size()) << std::endl;

	// Range-based for loop(C++11以上)での出力
	for (int i : iVector)
	{
		std::cout << i << std::endl;
	}

	// 要素の削除(末尾)
	// std::vector.pop_back();
	std::cout << std::format("{} を削除しました", iVector.back()) << std::endl;
	iVector.pop_back();

	// 要素の削除(任意)
	// std::vector.erase(iterator.position)
	itr = iVector.begin();
	itr += 4;
	std::cout << std::format("{} を削除しました", *itr) << std::endl;
	iVector.erase(itr);

	// 要素の削除(条件)
	// std::vector.erase(iterator.position)
	// remove_if()の戻り値はiterator.positionを返す
	std::cout << "3 以下の値を削除しました" << std::endl;
	iVector.erase
	(
		remove_if(iVector.begin(), iVector.end(), [](int& i) { return i <= 3; }),
		iVector.end()
	);

	// 要素の全削除
	// std::vector.clear()
	// std::vector.shrink_to_fit()はcapacity = sizeにする
	iVector.clear();
	iVector.shrink_to_fit();
    
	std::cout << std::format("確保しているメモリの容量 : {}", iVector.capacity()) << std::endl;
	std::cout << std::format("格納している要素の数 : {}", iVector.size()) << std::endl;

	// イテレータの操作での出力
	for (std::vector<int>::iterator itr = iVector.begin(); itr != iVector.end(); itr++)
	{
		std::cout << *itr << std::endl;
	}

	return 0;
}
```

#### remove_if
`remove_if(削除を開始する位置を示すイテレータ, 削除を終了する位置を示すイテレータ, ラムダ式);`
->条件を満たす要素をコンテナの後ろに移動し、新しい終端イテレータを返す

### List(C#)
```C#:Example.cs
using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        // 宣言・定義
        // List<型> 変数名 = new List<型>();
        List<int> iList = new List<int> { 1, 2, 3, 4, 5 };

        Console.WriteLine($"確保しているメモリの容量 : {iList.Capacity}");
        Console.WriteLine($"格納している要素の数 : {iList.Count}");

        // 要素の追加(末尾)
        // List.Add(値);
        iList.Add(6);

        Console.WriteLine($"確保しているメモリの容量 : {iList.Capacity}");
        Console.WriteLine($"格納している要素の数 : {iList.Count}");

        // 要素の追加(任意)
        // List.insert(index, 値);
        iList.Insert(iList.GetEnumerator().Current, -1);
        iList.Insert(iList.Count - 1, 7);

        Console.WriteLine($"確保しているメモリの容量 : {iList.Capacity}");
        Console.WriteLine($"格納している要素の数 : {iList.Count}");

        // foreachでの出力
        foreach (int i in iList)
        {
            Console.WriteLine(i);
        }

        // 要素の削除(末尾)
        // List.RemoveAt(List.Count - 1);
        Console.WriteLine($"{iList[iList.Count - 1]}を削除しました");
        iList.RemoveAt(iList.Count - 1);

        // 要素の削除(条件)
        // List.RemoveAll(ラムダ式)
        Console.WriteLine("3以下の値を削除しました");
        iList.RemoveAll(i => i <= 3);

        // 要素の全削除
        // List.clear()
        iList.Clear();

        Console.WriteLine($"確保しているメモリの容量 : {iList.Capacity}");
        Console.WriteLine($"格納している要素の数 : {iList.Count}");

        // イテレータの操作での出力
        itr = iList.GetEnumerator();
        while (itr.MoveNext())
        {
            Console.WriteLine(itr.Current);
        }
    }
}
```

## 連結リスト
**連結リスト**とは
- 自身の**前後のアドレスのみを知る**構造
- 配列とは違いバラバラな位置にデータが格納されているため、
**配列のような操作(`ランダムアクセス`)は不可能**
- **任意の位置への追加、削除**に優れている特徴を持つ

**用途 : 生成・削除が頻繁に行われるオブジェクト(シューティングゲームの弾など)の管理**

### list(C++)
```C++:Example.cpp
#include <iostream>
#include <list>
#include <format>

int main(void)
{
	// 宣言・定義
	// std::list<型> 変数名;
	std::list<int> iList = { 1, 2, 3, 4, 5 };

	std::cout << std::format("格納している要素の数 : {}", iList.size()) << std::endl;

	// 要素の追加(末尾)
	// std::list.push_back(値)
	iList.push_back(6);

	std::cout << std::format("格納している要素の数 : {}", iList.size()) << std::endl;

	// 要素の追加(先頭)
	// std::list.push_front(値)
	iList.push_front(-1);

	std::cout << std::format("格納している要素の数 : {}", iList.size()) << std::endl;

	// 要素の追加(任意)
	// std::list.insert(iterator.position, 値);
	iList.insert(iList.begin(), -2);
	iList.insert(iList.end(), 7);

    std::cout << std::format("格納している要素の数 : {}", iList.size()) << std::endl;

	// (※)イテレータの定義
	// std::list<型>::iterator 変数名;
	std::list<int>::iterator itr = iList.begin();
	advance(itr, 4);
	iList.insert(itr, 100);

	std::cout << std::format("格納している要素の数 : {}", iList.size()) << std::endl;

	// Range-based for loop(C++11以上)での出力
	for (int i : iList)
	{
		std::cout << i << std::endl;
	}

	// 要素の削除(末尾)
	// std::list.pop_back();
	std::cout << std::format("{} を削除しました", iList.back()) << std::endl;
	iList.pop_back();

	// 要素の削除(先頭)
	// std::list.pop_front();
	std::cout << std::format("{} を削除しました", iList.front()) << std::endl;
	iList.pop_front();

	// 要素の削除(任意)
	// std::list.erase(iterator.position)
	itr = iList.begin();
	advance(itr, 4);
	std::cout << std::format("{} を削除しました", *itr) << std::endl;
	iList.erase(itr);

	// 要素の削除(条件)
	// std::list.erase(iterator.position)
	// remove_if()の戻り値はiterator.positionを返す
	std::cout << "3 以下の値を削除しました" << std::endl;
	iList.erase
	(
		remove_if(iList.begin(), iList.end(), [](int& i) { return i <= 3; }),
		iList.end()
	);

	// 要素の全削除
	// std::list.clear()
	iList.clear();

	std::cout << std::format("格納している要素の数 : {}", iList.size()) << std::endl;

	// イテレータの操作での出力
	for (int i : iList)
	{
		std::cout << i << std::endl;
	}

	return 0;
}
```

### LinkedList(C#)
```C#:Example.cs
using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        // 宣言・定義
        // LinkedList<型> 変数名 = new List<型>();
        LinkedList<int> iLinkedList = new LinkedList<int>(new int[] { 1, 2, 3, 4, 5 });

        Console.WriteLine($"格納している要素の数 : {iLinkedList.Count}");

        // 要素の追加(末尾)
        // LinkedList.AddLast(値);
        iLinkedList.AddLast(6);

        Console.WriteLine($"格納している要素の数 : {iLinkedList.Count}");

        // 要素の追加(先頭)
        // LinkedList.AddFirst(値);
        iLinkedList.AddFirst(-1);

        Console.WriteLine($"格納している要素の数 : {iLinkedList.Count}");

        // 要素の追加(任意)
        // LinkedList.AddBefore(index, indexの要素の前に追加する値);
        // LinkedList.AddAfter(index, indexの要素の後に追加する値);
        iLinkedList.AddBefore(iLinkedList.First, -2);
        iLinkedList.AddAfter(iLinkedList.Last, 7);

        Console.WriteLine($"格納している要素の数 : {iLinkedList.Count}");

        // (※)イテレータの定義
        // LinkedListNode<型> 変数名;
        LinkedListNode<int> itr = iLinkedList.First;
        for (int i = 0; i < 4; i++) { itr = itr.Next; }
        iLinkedList.AddAfter(itr, 100);

        Console.WriteLine($"格納している要素の数 : {iLinkedList.Count}");

        // foreachでの出力
        foreach (int i in iLinkedList)
        {
            Console.WriteLine(i);
        }

        // 要素の削除(末尾)
        // LinkedList.RemoveLast();
        Console.WriteLine($"{iLinkedList.Last.Value}を削除しました");
        iLinkedList.RemoveLast();

        // 要素の削除(先頭)
        // LinkedList.RemoveFirst();
        Console.WriteLine($"{iLinkedList.First.Value}を削除しました");
        iLinkedList.RemoveFirst();

        // 要素の削除(任意)
        // LinkedList.Remove(itr);
        itr = iLinkedList.First;
        for (int i = 0; i < 4; i++) { itr = itr.Next; }
        Console.WriteLine($"{itr.Value}を削除しました");
        iLinkedList.Remove(itr);

        // 要素の削除(条件)
        // List.RemoveAll(ラムダ式)
        Console.WriteLine("3以下の値を削除しました");
        while (itr != null)
        {
            if (itr.Value <= 3)
            {
                iLinkedList.Remove(itr);
            }
            itr = itr.Next;
        }

        // 要素の全削除
        // LinkedList.clear()
        iLinkedList.Clear();

        Console.WriteLine($"格納している要素の数 : {iLinkedList.Count}");

        // イテレータの操作での出力
        itr = iLinkedList.First;
        while (itr != null)
        {
            Console.WriteLine(itr.Value);
            itr = itr.Next;
        }
    }
}
```

## 連想配列
**連想配列**とは
- pair型(**Key(鍵)**と**Value(値)**)によって管理されるデータの構造
- KeyとValueには**別々の型**が入る
- **二次元配列**に似た使用感

**用途 : エンティティの管理(IDと情報など)、オブジェクト属性(体力、攻撃力など)の管理など**

### map(C++)
```C++:Example.cpp
#include <iostream>
#include <map>
#include <string>
#include <format>

int main(void)
{
	// 宣言
	// std::map<keyとなる型, valueとなる型> 変数名;
	std::map<std::string, int> iMap;

	// 要素の追加(二次元配列方式)
	iMap["A"] = 0;
	iMap["B"] = 10;
	iMap["C"] = 20;

	std::cout << std::format("格納している要素の数 : {}", iMap.size()) << std::endl;

	// 要素の追加(std::make_pair<型, 型>())
	// std::map.insert(std::make_pair<型, 型>(key, value));
	iMap.insert(std::make_pair<std::string, int>("あ", 100));
    
	std::cout << std::format("格納している要素の数 : {}", iMap.size()) << std::endl;

	// 要素の追加(make_pair())
	// std::map.insert(std::make_pair(key, value));
	// ※ (型)値 はC言語のキャスト
	iMap.insert(std::make_pair((std::string)"い", 100));
    
	std::cout << std::format("格納している要素の数 : {}", iMap.size()) << std::endl;

	// 要素の追加(make_pair())
	// std::map.insert(std::make_pair(key, value));
	// ※ static_cast<std::string>値 はC++のキャスト
	iMap.insert(std::make_pair(static_cast<std::string>("う"), 100));
    
	std::cout << std::format("格納している要素の数 : {}", iMap.size()) << std::endl;
	
	// 要素の追加(emplace)
	// std::map.emplace(key, value);
	iMap.emplace("え", 100);
    
	std::cout << std::format("格納している要素の数 : {}", iMap.size()) << std::endl;

    // Range-based for loop(C++11以上)での出力
	for (std::pair<std::string, int> pair : iMap)
	{
		std::cout << std::format("Key : {}, Value : {}", pair.first, pair.second) << std::endl;
	}


	// 要素の削除
	// std::map.erase(key);
    std::cout << std::format("Key : え, Value : {} を削除しました", iMap["え"]) << std::endl;
	iMap.erase("え");

	// 勝手に新規作成しやがる場合
    // iMap.at(key)を使うとエラーを出せる
	int tmp = iMap["エラーにならない1"];
	std::cout << tmp << std::endl;

    // 新規作成->削除
	iMap.erase("エラーにならない2");

	// 要素の全削除
	// std::map.clear()
	iMap.clear();

	std::cout << std::format("格納している要素の数 : {}", iMap.size()) << std::endl;

	// イテレータの操作での出力
	for (std::map<std::string, int>::iterator itr = iMap.begin(); itr != iMap.end(); itr++)
	{
		std::cout << std::format("Key : {}, Value : {}", itr->first, itr->second) << std::endl;
	}

	return 0;
}
```

#### insertとemplaceの違い
insert : キーと値のペアの**コピーまたはムーブ**を行う
emplace : キーと値のペアの**直接構築**を行う

### Dictionary(C#)
```C#:Example.cs
using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        // 宣言
        // Dictionary<keyとなる型, valueとなる型> 変数名 = new Dictionary<同型, 同型>();
        Dictionary<string, int> iMap = new Dictionary<string, int>();

        // 要素の追加(二次元配列方式)
        iMap["A"] = 0;
        iMap["B"] = 10;
        iMap["C"] = 20;

        Console.WriteLine($"格納している要素の数 : {iMap.Count}");

        // 要素の追加(Add)
        // Dictionary.Add(key, value);
        iMap.Add("あ", 100);

        Console.WriteLine($"格納している要素の数 : {iMap.Count}");

        // foreachでの出力
        foreach (KeyValuePair<string, int> pair in iMap)
        {
            Console.WriteLine($"Key : {pair.Key}, Value : {pair.Value}");
        }

        // 要素の削除
        // Dictionary.Remove(key);
        iMap.Remove("あ");

        Console.WriteLine($"格納している要素の数 : {iMap.Count}");

        // 要素の全削除
        // Dictionary.Clear()
        iMap.Clear();

        Console.WriteLine($"格納している要素の数 : {iMap.Count}");
    }
}
```

## (※)イテレータ
**イテレータ**とは抽象化されたポインタのこと。

**用途 : コンテナクラスの操作**

先頭は**コンテナクラス型.begin()**、末尾は**コンテナクラス型.end()**で取得可能
※**末尾**とは最後の要素の次

*itrはイテレータが指し示す要素の値を取得できる

## 最後に
一応、UnityC#でのテスト(Console->Debug.Log)を行っていますが、
本家C#との違いがあればご指摘お願いします。