FC2ブログ
ヽ|∵|ゝ(Fantom) の 開発blog? ホーム »C#
カテゴリー「C#」の記事一覧

【C#】文字列 → float (浮動小数点) 変換でエラーが出るときは…  


 もう随分前の話になるが、フランス語圏の人から「VRM Live Viewer を使いたいのだが、起動ができない」というメッセージが来た。

 とりあえずアプリのバージョンと状況を聞いて確認してみたが、普通に起動できる。念の為、BOOTH にも出しているので DL して試したが、やはり同じ結果だ。

 しばらく何の事か全く見当が付かなかったのだが、根気よくスクショを送って貰ったり、動画を見せて貰ったりしているうちに、環境の違い(OS)くらいしかもう無いと意見が一致したので、彼は英語を、私はフランス語をインストールして OS を再起動してみた。

 そしたらどうだ。彼は「起動に成功した」と言い、私は彼が言っていたように起動に失敗した。

 原因は起動時に読み込まれる BVH ファイルで、そのファイルはテキストで書かれていて、小数点は "."(ドット) で書かれている。実はフランス語、アイスランド語など特定の言語圏では、小数点は "," (カンマ) で書くので、気の利いた OS (笑)がエラーを吐いていたのだ。

 かくして、ライブラリ作者に不具合報告をして、今ではそのフランスの VTuber もめでたく利用して貰っている。

特定の言語環境下において、BVHロードに失敗する

 まぁ、そんなこんなで「文字列を float (浮動小数点)に変換するなら、拡張メソッドを作っておいた方が良いよ」(※使うなら double 等も) という事で、簡単なものを紹介しておこう。



●フランス語、アイスランド語など特定の言語圏でも対応できる 文字列 → float (浮動小数点) 変換
using System;
using System.Globalization;

public static class Extensions //名前は任意
{
/// <summary>
/// 文字列 → float 変換 (CultureInfo.InvariantCulture 指定)
/// </summary>
/// <param name="s">変換元文字列</param>
/// <returns>変換後の float (失敗は 0)</returns>
public static float ToFloat(this string s)
{
if (float.TryParse(s, NumberStyles.Number, CultureInfo.InvariantCulture, out var value))
{
return value;
}
return 0f;
}


/// <summary>
/// 文字列 → float 変換 (CultureInfo.InvariantCulture 指定)
/// </summary>
/// <param name="s">変換元文字列</param>
/// <param name="result">変換成功時 = float 値 / 変換失敗時 = 0</param>
/// <returns>true = 変換成功 / false = 変換失敗</returns>
public static bool TryParseFloat(this string s, out float result)
{
if (float.TryParse(s, NumberStyles.Number, CultureInfo.InvariantCulture, out var value))
{
result = value;
return true;
}
result = 0;
return false;
}
}

●メインコード例
using System;

var s = "1.234";
Console.WriteLine("ToFloat : " + s.ToFloat());

if (s.TryParseFloat(out var result))
{
Console.WriteLine("TryParseFloat : " + result);
}
else
{
Console.WriteLine("TryParseFloat failed");
}

ToFloat : 1.234
TryParseFloat : 1.234

 結果だけ見ると何て事ないのだが、この関数なら、フランス語、アイスランド語など特定の言語圏でもエラーにはならない。

 ちなみに、float 変換のオプションに使っている「CultureInfo.InvariantCulture」は「カルチャに依存しない」(無視する) ものらしい。要するに OS の言語とは関係なく、常に一定(英語圏)で変換されるようだ。むしろそっちの方がデフォだと有り難いのだが…(例えば文字列比較の Equals 等もデフォだとカルチャが効くので言語によって結果が異なる可能性がある)。

 必要なら double 等、他の浮動小数点も作っておいた方が良いだろう。

 動作確認するには OS にフランス語を入れて、OS の言語設定でフランス語に設定した後で、OS を再起動して確かめるしかないが、特に最近はアプリが世界中で使われるような時代になったので、これは常備した方が良いだろう。実際私もフランスの人から報告貰うまでは考えもしなかった。



 実は、不具合報告って開発者にとっては非常に有用な情報なので「なんか動作がおかしいな…」と思ったら、連絡してあげると良い。我々はバグ取りのエキスパートでもあるので、すぐに原因究明から修正案まで出せると思う。

 実際、この不具合(特定の言語環境下において、BVHロードに失敗する) はライブライリ元の UniVRM が抱えていた問題だったので、これが Fix されると、フランス圏の人もアプリや VRM が利用できるようになったわけだしね(少なくとも BVH を使うアプリはそれまでフランス圏では利用できなかったハズ:3tene とかも BVH 機能入ってた気がする)。

 単純に不具合が直ると自分も幸せだし、他の多くの利用者も幸せになれるしね(笑)。





(関連記事)
【Unity】【C#】StartsWith, EndsWith が遅いのなら、Equals も遅いのか?
【Unity】【C#】範囲を指定できる Mathf.Repeat
【C#】【Unity】enum 型と string, int 型の相互変換など
【Unity】色形式:Unity の Color と Android の ARGB(int32) の相互変換をする


スポンサーサイト



category: C#

thread: プログラミング

janre: コンピュータ

tag: C#ライブラリ  C#リファレンス  算術関数 
tb: 0   cm: --

【C#】配列やコレクションの IsNullOrEmpty  


 ああ、そう言えば「配列 や List の TryGetValue」やったのに、もっとよく使うもの忘れてたな~、と思い出したので、とても簡単なものだが、あると非常に便利ものなので一応書いておこう。

 これは文字列で string.IsNullOrEmpy() をよく使うので、同じような感覚で、配列や List 等が null または 空 (Count = 0) でないかを調べるものがあれば、便利なのにな~と思ったので、昔作ったものだ。実際にあまりに頻繁に使うので、標準関数かと思い違いしてしまうほどだ(笑)。

 まぁたぶん、皆似たようなものを作ってる気がするが、まだ勉強しはじめたばかりの人もいるかもなので(面白いことに、もう随分たくさんの記事を書いたが、上級者向けより、初心者向けの記事の方がアクセスは多いものなのだ)、役に立つかはわからないが、とりあえず書いておこう。



●配列やコレクションの IsNullOrEmpty
using System;
using System.Collections.Generic;

public static class Extensions //名前は任意
{
/// <summary>
/// コレクションが null または Count = 0 か?
/// 2021/07/11 Fantom
/// http://fantom1x.blog130.fc2.com/blog-entry-399.html
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="collection">調べるコレクション</param>
/// <returns>true : null または Count = 0</returns>
public static bool IsNullOrEmpty<T>(this ICollection<T> collection)
{
return collection == null || collection.Count == 0;
}

/// <summary>
/// 配列が null または Length = 0 か?
/// 2021/07/11 Fantom
/// http://fantom1x.blog130.fc2.com/blog-entry-399.html
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="array">調べる配列</param>
/// <returns>true : null または Length = 0</returns>
public static bool IsNullOrEmpty<T>(this T[] array)
{
return (array == null || array.Length == 0);
}
}

●メインコード例
using System;
using System.Collections.Generic;

var emptyList = new List();
var list = new List() { 1, 2, 3 };
Console.WriteLine("emptyList : " + emptyList.IsNullOrEmpty());
Console.WriteLine("list : " + list.IsNullOrEmpty());

var emptyDic = new Dictionary();
var dic = new Dictionary()
{
{ "hoge", 100 },
{ "fuga", 200 },
};
Console.WriteLine("emptyDic : " + emptyDic.IsNullOrEmpty());
Console.WriteLine("dic : " + dic.IsNullOrEmpty());

var emptySet = new HashSet();
var set = new HashSet() { "hoge", "fuga" };
Console.WriteLine("emptySet : " + emptySet.IsNullOrEmpty());
Console.WriteLine("set : " + set.IsNullOrEmpty());

var emptyArray = new string[0];
var array = new string[] { "apple", "banana", "candy" };
Console.WriteLine("emptyArray : " + emptyArray.IsNullOrEmpty());
Console.WriteLine("array : " + array.IsNullOrEmpty());

emptyList : True
list : False
emptyDic : True
dic : False
emptySet : True
set : False
emptyArray : True
array : False

 これも本当になんてことないものなんだけどね。できればこれも標準関数に欲しいくらい(笑)。

 私はこれに NOT値(反転値)を求める IsExist() みたいな定義も加えて、よく使っている。

●!IsNullOrEmpty() (反転値) を返す IsExist()
//null でない、かつ Count > 0
public static bool IsExist(this ICollection collection) => !collection.IsNullOrEmpty();

//null でない、かつ Length > 0
public static bool IsExist(this T[] array) => !array.IsNullOrEmpty();

 これは「if (!list.IsNullOrEmpty())」みたいなものを「if (list.IsExist())」と「!」を入れないで済むので、少しわかりやすくなるので重宝してる。まぁ、日本語でも「〇〇でなくはない」みたいな否定の否定って意味がわかりずらいからね。その辺は好みで(笑)。






(関連記事)
【C#】配列 や List の TryGetValue
【Unity】【C#】配列・リストのシャッフル
【C#】2次元配列(ジャグ配列・多次元配列)のソート
【C#】多次元配列とジャグ配列(2次元配列)のサイズ(長さ)、相互変換など
【C#】配列やListなどの中身(要素)を見る拡張メソッド Dump


category: C#

thread: プログラミング

janre: コンピュータ

tag: C#ライブラリ  C#リファレンス  配列操作  連想配列 
tb: 0   cm: --

【C#】HashSet や Dictionary の AddRange  


 前回に引き続き「標準関数にあったら良いな」シリーズ(笑)。

 List には後から、要素を複数追加する AddRange() というメソッドがあるが、残念ながら、HashSetDictionary には無い。しかし、意外と複数の要素を追加したいときってあるんだよね。そんな時に便利な拡張メソッド。



●HashSet, Dictionary の AddRange
using System;
using System.Collections.Generic;

public static class Extensions //名前は任意
{
/// <summary>
/// 複数の要素を追加する(同じ値は上書きとなる)
/// ※null であっても、必ず生成される
/// 2021/06/13 Fantom
/// http://fantom1x.blog130.fc2.com/blog-entry-398.html
/// </summary>
/// <param name="set">追加先</param>
/// <param name="collection">追加ソース</param>
/// <returns></returns>
public static HashSet<T> AddRange<T>(this HashSet<T> set, IEnumerable<T> collection)
{
if (set == null)
set = new HashSet<T>();

if (collection != null)
{
foreach (var item in collection)
set.Add(item);
}
return set;
}

/// <summary>
/// 複数のキーと値ペアを追加する(同じキーは上書きとなる)
/// ※null であっても、必ず生成される.
/// 2021/06/13 Fantom
/// http://fantom1x.blog130.fc2.com/blog-entry-398.html
/// </summary>
/// <param name="dst">追加先</param>
/// <param name="src">追加ソース</param>
/// <returns></returns>
public static Dictionary<K, V> AddRange<K, V>(this Dictionary<K, V> dst, Dictionary<K, V> src)
{
if (dst == null)
dst = new Dictionary<K, V>();

if (src != null)
{
foreach (var item in src)
dst[item.Key] = item.Value;
}
return dst;
}
}

●メインコード例
using System;
using System.Collections.Generic;
using System.Linq;

var aSet = new HashSet<int>(Enumerable.Range(0, 5));
Console.WriteLine("Before : " + aSet.Dump());

var bSet = new HashSet<int>(Enumerable.Range(10, 5));
aSet.AddRange(bSet);
Console.WriteLine("After : " + aSet.Dump());

var cSet = new HashSet<int>(aSet).AddRange(bSet);
Console.WriteLine("c = a + b : " + cSet.Dump());

var aDic = new Dictionary<int, string>()
{
{ 0, "a" },
{ 1, "b" },
{ 2, "c" },
};
Console.WriteLine("Before : " + aDic.Dump());

var bDic = new Dictionary<int, string>()
{
{ 10, "x" },
{ 11, "y" },
{ 12, "z" },
};
aDic.AddRange(bDic);
Console.WriteLine("After : " + aDic.Dump());

var cDic = new Dictionary<int, string>(aDic).AddRange(bDic);
Console.WriteLine("c = a + b : " + cDic.Dump());

Before : [0, 1, 2, 3, 4]
After : [0, 1, 2, 3, 4, 10, 11, 12, 13, 14]
c = a + b : [0, 1, 2, 3, 4, 10, 11, 12, 13, 14]
Before : [[0, a], [1, b], [2, c]]
After : [[0, a], [1, b], [2, c], [10, x], [11, y], [12, z]]
c = a + b : [[0, a], [1, b], [2, c], [10, x], [11, y], [12, z]]

 要素をダンプしている Dump() は以前の記事からコピペして欲しい。

 少し標準関数の List.AddRange() と違うのは、void を返してるのではなく、コレクションを返してる所かな。こうすると、メソッドチェーンみたいにできるので、new と同時にいくつかの別コレクションを追加できたりするので、便利だ。

 とても簡単なものだけど、これもあると非常に有用なんだよね。私は結構大きなシステムを自作してるけど、大きくなるほど、こういった便利関数は欠かせなくなる(毎回似たような処理を書くのは大変な物量になるため)。実はプログラミングってこういう小さな工夫の積み重ねなんだよね(笑)。



 「これくらいのものなら、LINQ で書けばいいんじゃないの?」と思うかも知れないが、私はあまりオススメしない。というのは LINQ はそれなりにコストがかかるのと、GC を発生しやすいので、頻繁に使われるかも知れない拡張メソッド等は、割とベタな書き方をした方が速いし、GC も制御しやすいからだ。まぁ、string 系の処理はどのみち GC が発生するので使うこともあるが(しかし、できる限り StringBuilder を使う方が良い)、LINQ はどちらかというと、ランタイムで1回きりの初期化処理とか、頻繁には使わない処理用かな。特に Unity のような毎フレーム更新するような処理には気をつけて使った方が良いね。

コストのかかる操作を避ける





(関連記事)
【C#】配列 や List の TryGetValue
【C#】配列やListなどの中身(要素)を見る拡張メソッド Dump
【Unity】【C#】配列・リストのシャッフル
【C#】2次元配列(ジャグ配列・多次元配列)のソート
【C#】多次元配列とジャグ配列(2次元配列)のサイズ(長さ)、相互変換など


category: C#

thread: プログラミング

janre: コンピュータ

tag: C#ライブラリ  C#リファレンス  配列操作  連想配列 
tb: 0   cm: --

【C#】配列 や List の TryGetValue  


 今回はとても簡単なもので、意外と便利なもの。

 配列や List を扱う上で注意すべき点は、インデクス範囲外(Index was out of range) と null かどうかだが、毎回 null チェックや範囲外チェックをやるのは少し面倒である。

 Dictionary ではキーを用いて値の取得を試みる TryGetValue があるが、これと同じ様なものがあったら便利なのにな~、と思ったので作ってみた。



●配列, List の TryGetValue
using System;
using System.Collections.Generic;

public static class Extensions //名前は任意
{
/// <summary>
/// 配列 の TryGetValue
/// ・配列自体が null or インデクスが範囲外のとき false
/// 2021/05/17 Fantom
/// http://fantom1x.blog130.fc2.com/blog-entry-397.html
/// </summary>
/// <typeparam name="T">要素の型</typeparam>
/// <param name="array">対象の配列</param>
/// <param name="idx">取得するインデクス</param>
/// <param name="value">取得成功したときの値</param>
/// <returns>true = 取得成功</returns>
public static bool TryGetValue<T>(this T[] array, int idx, out T value)
{
value = default(T);
if (array == null || idx < 0 || array.Length <= idx)
return false;

value = array[idx];
return true;
}

/// <summary>
/// List の TryGetValue
/// ・List 自体が null or インデクスが範囲外のとき false
/// 2021/05/17 Fantom
/// http://fantom1x.blog130.fc2.com/blog-entry-397.html
/// </summary>
/// <typeparam name="T">要素の型</typeparam>
/// <param name="list">対象の List</param>
/// <param name="idx">取得するインデクス</param>
/// <param name="value">取得成功したときの値</param>
/// <returns>true = 取得成功</returns>
public static bool TryGetValue<T>(this List<T> list, int idx, out T value)
{
value = default(T);
if (list == null || idx < 0 || list.Count <= idx)
return false;

value = list[idx];
return true;
}
}

●メインコード例
using System;
using System.Collections.Generic;
using System.Linq;

int max = 50;
var array = Enumerable.Range(0, max).ToArray(); //0~49 まで
var list = Enumerable.Range(0, max).ToList(); //0~49 まで

int idxA = 25;
if (array.TryGetValue(idxA, out var resultA))
{
Console.WriteLine($"result value (Array) = {resultA}");
}
else
{
Console.WriteLine($"idx = {idxA} : Index was out of range or null (Array)");
}

int idxL = 50;
if (list.TryGetValue(idxL, out var resultL))
{
Console.WriteLine($"result value (List) = {resultL}");
}
else
{
Console.WriteLine($"idx = {idxL} : Index was out of range or null (List)");
}

//var res = list[100]; //Index was out of range

result value (Array) = 25
idx = 50 : Index was out of range or null (List)

 本当になんてことないものなんだけどね。できれば標準関数に欲しいくらい(笑)。

 例えば範囲外のインデクスのとき、予め Fallback 的な値を決めておく等、とても簡潔に書ける。

●例:範囲内なら配列の値を返し、範囲外なら 0 を返す
int[] arr = {0,1,2,3,4};
int GetOrFallback(int idx) => arr.TryGetValue(idx, out var res) ? res : 0;




 ところで少し話はずれるが、Dictionary で値の取得を試みるとき、どうやっているだろうか?

 例えば以下のような感じに書けると思うが、① と ② では実行速度が結構違う。

ContainsKey を用いてから取得
if (dic.ContainsKey(key))  //dic は Dictionary とする
{
var res = dic[key];
//ここで res を使った処理
}

TryGetValue で取得
if (dic.TryGetValue(key, out var res))  //dic は Dictionary とする
{
//ここで res を使った処理
}

 ① と ② は同じ結果にはなるが、実は実行速度は ① の方が約2倍かかる(遅い)。

 理由は簡単で、① は ContainsKey で1度 get, インデクサ(dic[key]) でもう1度 get の計2回 get (取得)するからである。TryGetValue は1回で済むので、その分速い。

 ほんのちょっとしたことでも、実行速度を改善できたりするので、特に高速なメインループ処理を実現したい時などに思い出してみると良い(Unity みたいな、毎フレーム回す処理には、徹底すれば意外と効果がある=アルゴリズム変更しない限りでは「手数を減らす」のが直接速度改善に繋がる)。





(関連記事)
【Unity】【C#】配列・リストのシャッフル
【C#】2次元配列(ジャグ配列・多次元配列)のソート
【C#】多次元配列とジャグ配列(2次元配列)のサイズ(長さ)、相互変換など
【C#】配列やListなどの中身(要素)を見る拡張メソッド Dump


category: C#

thread: プログラミング

janre: コンピュータ

tag: C#ライブラリ  C#リファレンス  配列操作 
tb: 0   cm: --

【C#】ソート済み List を動的に作る拡張メソッド  


 今回は二分探索によるリスト処理の高速化できる拡張メソッドを作ってみよう。

 普段、何気なく List に値を Add したり、Remove したりしてはいないだろうか?
 また検索というと、標準で使える List.ContainsList.FindList.FindIndex 等や、Linq の FistOrDefault などを使う機会も多いだろう。
 しかし、これらは逐次探索(O(n))のため、要素数に比例して、検索速度は遅くなる。特に1000を超えるデータを頻繁に扱うとなると、かなり実行速度に影響が出る。

 そのような場合、ケースにもよるのだが、List に追加する要素の順番は必要ないとき、または ID のように昇順で並んでいる方が便利なとき等、常に List をソート済みにしておくと、二分探索(BinaraySearch)が使えるようになる。
 二分探索のオーダーは O(log n) のため、要素が多くなっても、検索速度はそれほど落ちない。

 もし、ただの1回のみのソートで良いのなら、全て要素を追加した後に、単純に List.Sort や Linq の OrderBy を使う方が良いだろう。しかし、頻繁にデータを追加したり、削除したりする場合は、動的にソート済みリストを作っていく拡張メソッドを作っておくと便利だ。



●動的にソート済みリストに要素を追加、削除していく拡張メソッド
using System.Collections.Generic;

public static class Extentions //※名前は任意
{
/// <summary>
/// 昇順で要素を追加する
/// 2021/02/02 Fantom
/// http://fantom1x.blog130.fc2.com/blog-entry-392.html
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="list">対象リスト</param>
/// <param name="value">追加する要素</param>
/// <returns>true = 新規追加 / false = 既存あり(重複で追加)</returns>
public static bool AddSorted<T>(this List<T> list, T value)
{
int idx = list.BinarySearch(value);
if (idx < 0)
{
list.Insert(~idx, value);
return true;
}
list.Insert(idx, value);
return false;
}

/// <summary>
/// 昇順で要素をユニーク追加する
/// 2021/02/02 Fantom
/// http://fantom1x.blog130.fc2.com/blog-entry-392.html
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="list">対象リスト</param>
/// <param name="value">追加する要素</param>
/// <returns>true = 新規追加 / false = 既存あり(追加しない)</returns>
public static bool AddSortedUniq<T>(this List<T> list, T value)
{
int idx = list.BinarySearch(value);
if (idx < 0)
{
list.Insert(~idx, value);
return true;
}
return false;
}

/// <summary>
/// 昇順のリストから要素を削除
/// 2021/02/02 Fantom
/// http://fantom1x.blog130.fc2.com/blog-entry-392.html
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="list">対象リスト</param>
/// <param name="value">削除する要素</param>
/// <returns>true = 削除した / false = 既存なし(削除失敗)</returns>
public static bool RemoveSorted<T>(this List<T> list, T value)
{
int idx = list.BinarySearch(value);
if (idx >= 0)
{
list.RemoveAt(idx);
return true;
}
return false;
}
}

●メインコード例
using System;
using System.Collections.Generic;

string[] array =
{
"Apple", "Banana", "Candy", "Donuts", "Egg", "Fish", "Candy", "Egg"
};

array.Shuffle(); //※Shuffle は以前の記事を参照
Console.WriteLine(array.Dump()); //※Dump は以前の記事を参照

var list1 = new List(array.Length);
var list2 = new List(array.Length);
foreach (var item in array)
{
list1.AddSorted(item); //昇順で追加
list2.AddSortedUniq(item); //昇順でユニーク追加
}

Console.WriteLine("list1 : " + list1.Dump()); //※Dump は以前の記事を参照
Console.WriteLine("list2 : " + list2.Dump()); //※Dump は以前の記事を参照

//昇順リストから削除
list1.RemoveSorted("Candy");
list1.RemoveSorted("Egg");
Console.WriteLine("list1 (Removed) : " + list1.Dump()); //※Dump は以前の記事を参照
Console.WriteLine("list2 (Removed) : " + list2.Dump()); //※Dump は以前の記事を参照

//二分探索
int idx1 = list1.BinarySearch("Candy");
Console.WriteLine($"idx1 = {idx1}");
int idx2 = list1.BinarySearch("Egg");
Console.WriteLine($"idx2 = {idx2}");

[Banana, Egg, Apple, Candy, Egg, Donuts, Fish, Candy]
list1 : [Apple, Banana, Candy, Candy, Donuts, Egg, Egg, Fish]
list2 : [Apple, Banana, Candy, Donuts, Egg, Fish]
list1 (Removed) : [Apple, Banana, Candy, Donuts, Egg, Fish]
list2 (Removed) : [Apple, Banana, Candy, Donuts, Egg, Fish]
idx1 = 2
idx2 = 4

 DumpShuffle の拡張メソッドは以前に作ったものだ。

Dump の拡張メソッド
Shuffle の拡張メソッド

 どのように追加されているかに注目して欲しい。常に昇順に並び替えたリストと同義となるため、追加するたびソート(List.Sort)する必要はもちろん無い。
 取得するときは List.BinarySearch が使えるので、ちょっとやそっと要素数が増えた所で、検索速度が落ちることはない(実際に 100000 [10万] くらいの要素数があっても、検索速度は速い)。

 また、この例ではコンストラクタで new List(int capacity) を使っているが、これは予め格納できるサイズを確保しておくコンストラクタだ。色々なコードを見ていても、ほとんどが引数なし new List() で使われているが、実はこれはとても重要だ。と言うのは、List の様な動的に増やせるコレクションというのは、現在格納できるサイズを超えると、メモリの再割当てをし(サイズ拡大)、コピーする動作を頻繁に繰り返すからだ。要素が多くなると、負荷がかかるのは言うまでもない。はじめからわかっている、または想定できるなら、capacity を指定した方が、要素を追加するとき、無駄な処理が走らない(Dictionary なども同じ)。

 以下は Unity 上で、毎フレーム逐次検索(List.ContainsList.Find)していた箇所を、二分探索探索に置き換えただけのもので、パフォーマンスを比較してみたものだ。
 青い部分はスクリプトの負荷で、激減していることが分かる。実際にほんの数行置き換えただけなのだが、圧倒的なパフォーマンスを出すことができることさえある(この時は約5倍以上になった)。



 ここでは、List.BinarySearch しか使ってないが、以前に紹介した LowerBounds, UpperBounds も併用して使うと良いだろう。

【C#】 LowerBound, UpperBound (二分探索)

 二分探索は上手く使えば、かなりのパフォーマンス改善になる場合があることも少なくない。開発現場では簡潔だからか Linq が使われることが多い気がするが、実はとても遅く、GCを発生させ易いので、利用には注意が必要だ(要は使い処である)。

Unity のパフォーマンスに関する推奨事項 - コストのかかる操作を避ける
LINQとfor, ListとArray(配列)での実行速度を比較してみる
【Unity】CPUプロファイラでパフォーマンスを改善する - GCの発生要因を減らす






(関連記事)
【C#】配列やListなどの中身(要素)を見る拡張メソッド Dump
【Unity】【C#】配列・リストのシャッフル
【C#】二分探索の実装(範囲インデクス指定実装)
【C#】 LowerBound, UpperBound (二分探索)
【Ruby】二分探索, lower_bound, upper_bound
【Unity】【C#】LINQとfor, ListとArray(配列)での実行速度を比較してみる
【C#】連想配列(Dictionary)を値 or キーでソート
【C#】2次元配列(ジャグ配列・多次元配列)のソート
【C#】多次元配列とジャグ配列(2次元配列)のサイズ(長さ)、相互変換など


category: C#

thread: プログラミング

janre: コンピュータ

tag: C#ライブラリ  配列操作  二分探索 
tb: 0   cm: --


プロフィール

Social

検索フォーム

全記事一覧

カテゴリ

ユーザータグ

最新記事

リンク

PR

▲ Pagetop