Позвольте мне дополнить Полезный ответ TheIncorrigible1 с более широкой перспективой:
tl; др
Большую часть времени вы хотите [ordered] @{ ... }
([System.Collections.Specialized.OrderedDictionary]
) (PSv3 +):
- Он обеспечивает перечисление записей в том порядке, в которомони были определены (также отражено в свойствах коллекции
.Keys
и .Values
). - Он также позволяет получить доступ к записям по index , как к массиву.
Как правило, вы можете использовать [ordered] @{ ... }
взаимозаменяемо с @{ ... }
, обычной хеш-таблицей, [hashtable]
aka [System.Collections.Hashtable]
, потому что оба типа реализуют [IDictionary]
interface , как обычно набираются параметры, принимающие хеш-таблицы.
Потеря производительности, которую вы платите за использование [ordered]
, ничтожна.
Немного предыстории:
По техническим причинам наиболее эффективная реализация хеш-таблицы (хэш-таблицы) означает пусть порядок записей будет результатом подробностей реализации , без гарантии какого-либо конкретного заказа для вызывающей стороны.
Это хорошо для случаев использования, когда все, что вы делаете, это выполняете изолированный поиск по ключу, где упорядочение среди ключей (записей) не имеет значения.
Однако, часто вы заботитесь о порядке записей:
в простейшем случае для отображения целей;что-то смущает, когда видят порядок определений;например:
@{ one = 1; two = 2; three = 3 }
Name Value
---- -----
one 1
three 3 # !!
two 2
, что более важно, перечисление записей может быть предсказуемым для дальнейшей программной обработки;например (примечание: строго говоря, порядок свойств не имеет значения в JSON, но он снова важен для наблюдателя):
# No guaranteed property order.
PS> @{ one = 1; two = 2; three = 3 } | ConvertTo-Json
{
"one": 1,
"three": 3, # !!
"two": 2
}
# Guaranteed property order.
PS> [ordered] @{ one = 1; two = 2; three = 3 } | ConvertTo-Json
{
"one": 1,
"two": 2,
"three": 3
}
К сожалению, хеш-таблица PowerShell-литеральный синтаксис, @{ ... }
, не по умолчанию [ordered]
[1] , но уже слишком поздно это менять.
Существует один контекст, в котором [ordered]
подразумевается подразумевается , однако: если вы приведете хеш-таблицу литерал к [pscustomobject]
для создания пользовательского объекта:
[pscustomobject] @{ ... }
- синтаксический сахар для [pscustomobject] [ordered] @{ ... }
;то есть свойства результирующего пользовательского объекта упорядочиваются на основе порядка ввода в литерале хеш-таблицы;например:
PS> [pscustomobject] @{ one = 1; two = 2; three = 3 }
one two three # order preserved!
--- --- -----
1 2 3
Обратите внимание, однако, что этот работает только в точности так, как показано выше: если приведение применяется напрямую к хеш-таблице литерал ;если вы используете переменную , чтобы сначала сохранить хеш-таблицу, или если вы просто заключили литерал в (...)
, порядок будет потерян:
PS> $ht = @{ one = 1; two = 2; three = 3 }; [pscustomobject] $ht
one three two # !! Order not preserved.
--- ----- ---
1 3 2
PS> [pscustomobject] (@{ one = 1; two = 2; three = 3 }) # Note the (...)
one three two # !! Order not preserved.
--- ----- ---
1 3 2
Следовательно, если высначала создайте хеш-таблицу , а затем , а затем приведите ее к [pscustomobject]
, вы должны начать с [ordered]
хеш-таблицы , чтобы получить предсказуемое упорядочение свойств;этот метод полезен, потому что проще создавать записи хеш-таблицы, чем добавлять свойства к пользовательскому объекту;Например:
$oht = [ordered] @{} # Start with an empty *ordered* hashtable
# Add entries iteratively.
$i = 0
foreach ($name in 'one', 'two', 'three') {
$oht[$name] = ++$i
}
[pscustomobject] $oht # Convert the ordered hashtable to a custom object
Наконец, обратите внимание, что [ordered]
может применяться только к хеш-таблице literal ;Вы не можете использовать его для преобразования ранее существовавшей обычной хеш-таблицы в упорядоченную (что в любом случае не имело бы никакого смысла, поскольку у вас нет определенного порядка для начала):
PS> $ht = @{ one = 1; two = 2; three = 3 }; [ordered] $ht # !! Error
...
The ordered attribute can be specified only on a hash literal node.
...
На заметку: Ни упорядоченные, ни обычные хеш-таблицы не перечисляют свои записи при отправке через конвейер ;они отправляются целиком.
Чтобы перечислить записи, используйте метод .GetEnumerator()
;Например:
@{ one = 1; two = 2; three = 3 }.GetEnumerator() | ForEach-Object { $_.Value }
1
3 # !!
2
Что касается влияния на производительность при использовании [ordered]
:
Как отмечалось, оно незначительно ;Вот некоторые примеры времени, усредненные по 10000 прогонов, с использованием Time-Command
:
Time-Command -Count 10,000 { $ht=@{one=1;two=2;three=3;four=4;five=5;six=6;seven=7;eight=8;nine=9}; foreach($k in $ht.Keys){$ht.$k} },
{ $ht=[ordered] @{one=1;two=2;three=3;four=4;five=5;six=6;seven=7;eight=8;nine=9}; foreach($k in $ht.Keys){$ht.$k} }
Примеры времени (Windows PowerShell 5.1 в Windows 10, одноядерная виртуальная машина):
Command TimeSpan Factor
------- -------- ------
$ht=@{one=1;two=2;th... 00:00:00.0000501 1.00
$ht=[ordered] @{one=... 00:00:00.0000527 1.05
То есть [ordered]
составило всего 5% замедления.
[1] Incorrigible1 указывает на один хитрый аспект, характерный для [ordered]
хеш-таблиц :
с числовыми клавишами , различая между key и index могут стать хитрыми;, чтобы принудительно интерпретировать число как ключ , приведите его к [object]
:
# Ordered hashtable with numeric keys.
PS> $oht = [ordered] @{ 1 = 'one'; 2 = 'two' }
PS> $oht[1] # same as $oht.1 - interpreted as *index* -> 2nd entry
two
PS> $oht[[object] 1] # interpreted as *key* -> 1st entry.
one
Тем не менее, цифровые клавиши не являются общими имне преимущество от использования предсказуемого перечисления перевешивает эту незначительную проблему.
Тип платформы .NET, лежащий в основе [ordered]
, System.Collections.Specialized.OrderedDictionary
, был доступен начиная с v1, поэтому PowerShell мог бы выбрать его в качестве реализации по умолчанию для@{ ... }
с самого начала, даже в PowerShell v1.
Учитывая приверженность PowerShell к обратной совместимости, изменение значения по умолчанию больше не вариант, поскольку это может нарушить существующий код.