PowerShellの配列、ArrayList、およびコレクション:ベストプラクティス

よくPowerShellスクリプトを書く際には、アイテムのセットを保存する方法が必要です。これを実現する一般的な方法の1つは、配列またはArrayListと呼ばれる特定のタイプを使用することです。しかし、配列とは何でしょうか?配列は、アイテムのコレクションを保存するために設計されたデータ構造です。これには、同じ型と異なる型のアイテムの両方が含まれることができます。

配列は、さまざまなプログラミング言語で使用されており、PowerShellも例外ではありません。配列を作成、操作、最適化するためのさまざまな方法があります。この記事では、ArrayList、配列、コレクションについて学び、それらをPowerShellで適用する際のベストプラクティスについても紹介します。

前提条件/要件

PowerShell言語自体との作業に関しては、環境の前提条件はありません。PowerShellが実行されているWindows PCがあれば十分です。具体的には、

  • Windows PowerShell 3以降
  • .NET Framework 4.5以降

このようなヒントがもっと欲しいですか?私の個人的なPowerShellブログをチェックしてみてください:https://nkasco.com/FriendsOfATA

PowerShellでの配列の作成

PowerShellで配列を作成する方法はさまざまです。以下に示すような処理する必要がある名前のリストがあると想定してみましょう。

John
Susie
Jim
Johnny
Carrie

コンマで区切られた要素を使用して配列を構築する

配列を作成する最も基本的な方法は、下記のように既知の入力をコンマで区切って変数に割り当てることです。

$BasicArray = "John", "Susie", "Jim", "Johnny", "Carrie"

PowerShellのすべてのオブジェクトで使用できるGetType()メソッドを実行すると、以下のBaseTypeプロパティによって、正常に配列が作成されたことがわかります。

PS51> $BasicArray.GetType()

IsPublic IsSerial Name                                     BaseType                                                    
-------- -------- ----                                     --------                                                    
True     True     Object[]                                 System.Array

サブ式演算子を使用する

サブ式演算子を使用してPowerShellで配列を作成することもできます。この概念は、配列に追加されるアイテムの数を事前に知らない場合によく使用されます。結果は、作成時に0個または複数のアイテムを含む場合があります。

以下に、要素がゼロ個の$MyArrayという名前の配列が作成されていることがわかります。

#サブ式演算子を使用して空の配列を作成する
PS51> $MyArray = @()
PS51> $MyArray.count
0

範囲演算子を使用する

上記の例のように、配列は文字列の保存に限られません。整数などの他のオブジェクト型でも配列を作成することができます。

連続した整数の配列が必要な場合は、範囲演算子..を使用してショートカットを取ることもできます。以下のコードでは、1行で整数2から5までの配列が作成されていることがわかります。

PS51> $NumberedArray = 2..5
PS51> $NumberedArray
2
3
4
5

PowerShell ArrayListコレクションの作成

PowerShell ArrayListを使用すると、アイテムのリストをPowerShellで保存することもできます。ArrayListクラスは.NETのSystem.Collections名前空間の一部です。このタイプの新しいオブジェクトを作成することで、ArrayList内にオブジェクトを保存できます。

以下の例では、New-Objectコマンドレットを使用するか、標準配列をArrayListオブジェクトにキャストすることで、明示的にArrayListオブジェクトを作成する必要があることがわかります。

この場合、BaseTypeはオブジェクトであり、一方、上記の例ではBaseTypeは配列であり、Objectクラスからの継承を示しています。最終的に、PowerShellは.NETの型システムへのアクセスを提供しています。

PS51> $MyArrayList = New-Object -TypeName "System.Collections.ArrayList"
# 配列をArrayListとしてキャストすることも可能です
PS51> $MyArrayList = [System.Collections.ArrayList]@()
PS51> $MyArrayList.GetType()

IsPublic IsSerial Name                                     BaseType                                                    
-------- -------- ----                                     --------                                                    
True     True     ArrayList                                System.Object

配列への要素の追加

配列を作成する際、要素をすべて定義するか、アドホックに追加することができます。

既存のコレクションに要素を追加するには、+=演算子またはAddメソッドを使用できます。ただし、それらの動作には大きな違いがあることを知っておいてください。

@()を使用して標準の配列を作成する場合、+=演算子を使用しますが、ArrayListに要素を追加する場合はAddメソッドを使用します。これらの方法の違いは、+=演算子が実際には既存の配列を破壊し、新しいアイテムを持つ新しい配列を作成する点にあります。

以下の例では、配列またはArrayListのIsFixedSizeプロパティを参照して、イミュータブルなものとそうでないものを判断できます。

PS51> $BasicArray.IsFixedSize
True

PS51> $MyArrayList.IsFixedSize
False

基本的な配列は固定サイズのコレクションであるため、変更することはできません。

Add()メソッドを固定サイズの配列で使用すると、固定サイズのためエラーが発生します。以下では、配列に項目を追加する方法のいくつかの例を示します。

#動作しません
$BasicArray.Add("Nate")

#動作します
$BasicArray += "Nate"
$MyArrayList.Add("Nate")
$MyArrayList += "Nate"

配列から項目を削除する方法

配列に項目を追加する方法がわかったので、配列から項目を削除する方法についても説明します。

基本的な配列は固定されているため、項目を削除することはできません。代わりに、完全に新しい配列を作成する必要があります。たとえば、含めたい要素のみに一致する条件文を作成して、配列から単一の要素を削除することができます。以下に例を示します。

$NewBasicArray = $BasicArray -ne "Nate"

ArrayListは固定されていないため、Remove()メソッドを使用して要素を削除することができます。頻繁に項目を追加/削除する予定がある場合、ArrayListを使用すると便利な場合があります。

$MyArrayList.Remove("Nate")

配列またはArrayListから特定の項目を取得する方法

配列またはArrayListから特定の項目を取得するには、さまざまな方法があります。PowerShellの他のオブジェクトと同様に、オブジェクトを呼び出すだけで配列のすべての要素にアクセスできます。

PS51> $BasicArray
John
Susie
Jim
Johnny
Carrie

たとえば、最初の要素のみを取得する場合、配列の最初の要素を表す0の起点を持っています。以下に示すように、インデックス番号を角かっこで指定して、配列の最初の要素を取得します。

PS51> $BasicArray[0]
John

逆に、配列から最後のX個の要素を呼び出すためにハイフン(マイナス指示子)を使用してインデックスを逆に参照することもできます。配列の最後の要素を見つける一般的な方法は、以下に示すように-1を使用することです。

PS51> $BasicArray[-1]
Carrie

前述の範囲演算子は、要素の呼び出し方法と同じ方法で配列のオブジェクトを取得するためにも使用することができます。たとえば、$BasicArray配列から最初の4つの名前を取得したいとします。

以下のように、0から3の範囲のインデックスを指定すると、最初の4つの要素が返されます。

PS51> $BasicArray[0..3]
John
Susie
Jim
Johnny

PowerShellで配列を最適化する

配列の作成と操作方法についての基礎ができたので、どれを使用するべきでしょうか?その答えを見つけるために、Measure-Commandコマンドレットのいくつかの例を実行してみましょう。 Measure-Commandコマンドレットを使用することで、コマンドが要素を処理する際にかかる時間をよりよく理解することができます。

一般的に言って、小さなオブジェクトのコレクションを持っている場合、配列の操作方法にはあまり違いを感じないでしょう。ただし、大きなオブジェクトのコレクションを持っている場合は、最適な結果を得るために違いを理解することが重要です。

さきほど学んだ+=Add()メソッドの違いを、50,000個のアイテムを含むループで適用してみましょう。

最初に、以下のように空の配列と空のArrayListを作成します。

PS51> $MyArray = @()
PS51> $MyArrayList = [System.Collections.ArrayList]@()

次に、範囲演算子とforeachループを使用して、各コレクションに50,000の要素を追加します。

@(0..50000).foreach({$MyArray += $_})
@(0..50000).foreach({$MyArrayList.Add($_)})

最後に、コマンドを式でラップし、その式をMeasure-Commandコマンドレットに渡します。 Measure-Commandを使用して式を実行することで、各プロセスの実行にかかる時間を確認できます。

前に学んだように、+=は実際には固定された配列に追加するのではなく、新しい配列を作成します。

PS51> Measure-Command -Expression {@(0..50000).foreach({$MyArray += $_})}
Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 59
Milliseconds      : 58
Ticks             : 590585963
TotalDays         : 0.000683548568287037
TotalHours        : 0.0164051656388889
TotalMinutes      : 0.984309938333333
TotalSeconds      : 59.0585963
TotalMilliseconds : 59058.5963

PS51> Measure-Command -Expression {@(0..50000).foreach({$MyArrayList.Add($_)})}
Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 0
Milliseconds      : 139
Ticks             : 1399989
TotalDays         : 1.62035763888889E-06
TotalHours        : 3.88885833333333E-05
TotalMinutes      : 0.002333315
TotalSeconds      : 0.1399989
TotalMilliseconds : 139.9989

結果は?約60秒対139 ミリ秒です!

大規模なコレクションでは、固定サイズの配列の代わりにArrayListを活用する方がはるかに高速です。

これは基本的な例ですが、処理中にコードが何を行っているかを理解することの重要性を強調しています。適切に理解されていない場合、ユーザーエクスペリエンスが低下する可能性があります。

ArrayListではなく配列を使用することで利益を得ることができる既存のスクリプトがある場合、これは一晩での改善の絶好の機会となります!

さらなる読み物

このようなヒントをもっと知りたいですか?私の個人的なPowerShellブログをチェックしてください:https://nkasco.com/FriendsOfATA

Source:
https://adamtheautomator.com/powershell-array/