参考: いろいろな言語での Map, Dictionary 的なものの名前 - Qiita
上の記事を見ていて、連想配列系の構文でどんなリテラル記法が使われているのかが気になったので、リテラル記法に絞って順不同(思い付いた順とも言う)で調べてみました。メジャーな言語のほか、新し目の言語もチェックしてみました。
あくまで連想配列系構文の基本的なリテラル記法を知りたかったので、細かな機能やヘルパー関数などについては省略しています。サンプルコードが洗練されてないのはご容赦🙇。
参考: 連想配列 - Wikipedia
map/dict/hash専用のリテラルがある言語とない言語でざっくり分けました。また、メソッドなどを用いるアクセスは最小限にとどめています。検証には主に以下のサイトを使いました。
- サイト: LabStack
間違いがありましたら@hachi8833までお知らせください。
- 専用の生成リテラル記法のある言語
- Ruby、Crystal、Elixir、Python、Perl、JavaScript、TypeScript、Dart、Go、V言語、Swift、PHP、Groovy、Lua
- 専用の生成リテラル記法のない言語
- Java、Kotlin、C#、Rust、Scala、Julia、Ocaml、C言語/C++
⚓専用のリテラル記法のある言語
⚓Ruby
参考: class Hash (Ruby 2.7.0 リファレンスマニュアル)
- 名称: hash
- キーは
シンボル:
でも表記できる(現在は主流)- 文字列キーとシンボルキーは文字が同じでも等価にならない点に注意
- 追加したキーバリューの順序が
each
などで保たれる点が特徴
# リテラルによる生成
hash1 = { 'apple' => 'りんご', 'peach' => 'もも' }
# リテラルによる生成(キーがシンボルの場合)
hash2 = { apple: 'りんご', peach: 'もも' }
- 実行可能なサンプルコード
hash1 = { 'apple' => 'りんご', 'peach' => 'もも' }
hash2 = { apple: 'りんご', peach: 'もも' }
# 参照
hash1['apple']
a = hash2[:apple]
puts a # 出力用
# 更新
hash2[:apple] = "林檎"
puts hash2 # 出力用
# 追加
hash2[:pear] = "なし"
puts hash2 # 出力用
# 削除
hash2.delete(:pear)
puts hash2 # 出力用
⚓Crystal
参考: Hash · GitBook
CrystalはRubyコンシャスな文法なので、Rubyととてもよく似ていますが、Rubyにない書き方もできます。
- 名称: hash
- シンボルをキーにする場合でも
=>
は省略できない
# リテラルによる生成
hash = { "apple" => "りんご", "peach" => "もも" }
hash2 = { :apple => "りんご", :peach => "もも" }
以下のようにすることでキーや値の型を明示的に指定できるようです。
# キーはStringかSymbol、値はStringかInt32
hash = Hash(String | Symbol, String | Int32){"foo" => "bar", :baz => 1}
以下のようにリテラルを使わずに生成もできます。
hash = Hash(typeof("foo", "bar"), typeof(1, "baz")).new
hash["foo"] = 1
hash["bar"] = "baz"
# 上は以下と同等
Hash{"foo" => 1, "bar" => "baz"}
- 実行可能なサンプルコード
hash = { :apple => "りんご", :peach => "もも" }
# 参照
a = hash[:apple]
puts a # 出力用
# 更新
hash[:apple] = "林檎"
puts hash # 出力用
# 追加
hash[:pear] = "なし"
puts hash # 出力用
# 削除
hash.delete(:pear)
puts hash # 出力用
⚓Elixir
参考: Keyword lists and maps - Elixir
参考: 急いで覚えるElixir: Enumerable編 - Akatsuki Hackers Lab | 株式会社アカツキ(Akatsuki Inc.)
RubyコンシャスといえばElixirもそうみたいですね。map、keyword listとありますが、どちらもdictとして振る舞えます。ここではmapのみを取り上げます。
- 名称: map(dict)
%{}
で生成- キーに任意の値を使える
- 破壊的操作はできず、
[] =
で更新することもできない - シンボルをキーにする場合でも
=>
は省略できない
map = %{ :apple => "りんご", :peach => "もも" }
- 実行可能なサンプルコード
map = %{ :apple => "りんご", :peach => "もも" }
# 参照
m = map[:apple]
IO.puts m # 出力用
# 参照2
m = Map.get(map, :apple)
IO.puts m # 出力用
# 更新
u = Map.put(map, :apple, "林檎")
IO.inspect u # 出力用
a = Map.put(map, :pear, "なし")
IO.inspect a # 出力用
# 削除
d = Map.delete(map, :pear)
IO.inspect d # 出力用
⚓Python
参考: 組み込み型 — Python 3.8.4rc1 ドキュメント
- 名称: dict(マッピング型)
# リテラルによる生成
d = {'apple': 'りんご', 'peach': 'もも'}
- 実行可能なサンプルコード
d = {'apple': 'りんご', 'peach': 'もも'}
# 参照
a = d['apple']
print(a) # 出力用
# 更新
d['apple'] = "林檎"
print(d)
# 更新2(このappleは引用符で囲めない)
d.update(apple="林檎")
print(d) # 出力用
# 追加
d['pear'] = "なし"
print(d) # 出力用
# 削除
del d['pear']
print(d) # 出力用
⚓Perl
- 名称: hash
- 生成時のハッシュ変数に
%
を付けるのが特徴 - 生成リテラルに
()
を使う - ハッシュ変数へのアクセス時は
%
ではなく$
# リテラルによる生成
my %h = ('apple' => 'りんご', 'peach' => 'もも');
- 実行可能なサンプルコード
use strict;
use warnings;
$, = "\n"; # 出力用
my %h = ('apple' => 'りんご', 'peach' => 'もも');
# 参照
$a = $h{'apple'};
print $a, "\n"; # 出力用
# 更新
$h{'apple'} = "林檎";
print %h, "\n"; # 出力用
# 追加
$h{'pear'} = "なし";
print %h, "\n"; # 出力用
# 削除
delete $h{'pear'};
print %h, "\n"; # 出力用
⚓JavaScript
参考: Map - JavaScript | MDN
参考: キー付きコレクション - JavaScript | MDN
参考: Object - JavaScript | MDN
参考: Object.fromEntries()でオブジェクトをmapする - 藤 遥のブログ
昔ながらのObject
と、機能が多く新しいMap
があります。ここではObject
を扱いました。Object
を連想配列と呼んでいいのかどうか判断がつかなかったのですが、知りたいのはリテラルなのでとりあえずここに置きました。
- 名称:
Object
(オブジェクトリテラル) - キーは「プロパティ」と呼ばれる
obj.pear = "なし";
のように、存在しないプロパティ(キーに相当)をいきなり書いて追加できるのが特徴
// リテラルによる生成
const obj = {apple: 'りんご', peach: 'もも'};
- 実行可能なサンプルコード
const obj = {apple: 'りんご', peach: 'もも'};
console.log(obj) // 出力用
// 参照(以下の2つは同じ)
obj['apple'];
obj.apple;
// 更新(以下の2つは同じ)
obj['apple'] ="林檎";
obj.apple = "林檎"
console.log(obj) // 出力用
// 追加(以下の2つは同じ)
obj['pear'] = "なし";
obj.pear = "なし";
console.log(obj) // 出力用
// 削除
delete obj.pear;
console.log(obj) // 出力用
なおMap
には生成用のリテラル構文がなく、new Map([ [キー, 値]... ])
で生成します。Object.fromEntries()
を使うとObject
に変換できます。
const map = new Map([ ['apple', 'りんご'], ['peach', 'もも']]);
const obj = Object.fromEntries(map);
⚓TypeScript
参考: TypeScriptの型: 辞書型を定義する (Dictionary)|まくろぐ
リテラル記法はJavaScriptのを使っていますが、独自の辞書型も定義できるので、ここに置いていいかどうか迷っています(追記: この{ [expr]: value }
という記法はECMAScript 2015以降でも使えると教わりました)。
- 名称: 辞書オブジェクト(ユーザー定義)
interface
を用いて、キーや値の型を指定した辞書型をユーザーが定義できる- (JavaScriptのオブジェクトリテラル記法に乗っかった形だが、オブジェクト型とは異なるらしい)
interface UserDictionary {
[id: string]: string;
}
const dic: UserDictionary = {'apple': "りんご", 'peach': "もも"};
- 実行可能なサンプルコード
interface UserDictionary {
[id: string]: string;
}
const dic: UserDictionary = {'apple': "りんご", 'peach': "もも"};
console.log(dic); // 出力用
// 参照1
console.log(dic['apple']);
// 参照2
console.log(dic.apple);
// 更新
dic['apple'] = "林檎";
console.log(dic); // 出力用
// 追加
dic['pear'] = "なし";
console.log(dic); // 出力用
// 削除
delete dic['pear'];
console.log(dic); // 出力用
⚓Dart
参考: コレクション – Dart逆引きリファレンス | Developers.IO
参考: 【Dart】Mapの順番(HashMap, LinkedHashMap, SplayTreeMap) - のんびり精進
参考: Dart 2 Language Guide
Map、LinkedHashMap、HashMap、SplayTreeMapというものがあるそうです。ここではMapのみを取り上げます。
- 名称: Map
- JavaScriptのようなドット表記ではアクセスできない
// リテラルによる生成
var a = {'apple': 'りんご', 'peach': 'もも'};
// この場合Map<String, String>と推論される
- 実行可能なサンプルコード
void main() {
var a = {'apple': 'りんご', 'peach': 'もも'};
// 参照
print(a['apple']);
// 更新(以下の2つは同じ)
a['apple'] ="林檎";
print(a); // 出力用
// 追加(以下の2つは同じ)
a['pear'] = "なし";
print(a); // 出力用
a.remove('pear');
print(a); // 出力用
}
⚓Go
参考: Goプログラミング言語仕様 - golang.jp
参考: 逆引きGolang (マップ)
- 名称: map
// map[]とリテラルによる生成
m := map[string]int{ "apple": "りんご", "peach": "もも" }
// 参考: 型定義のみの場合
var m map[string]int
// 参考: makeにはリテラルを直接書けない
m = make(map[string]int, 20)
- 実行可能なサンプルコード
package main
import (
"fmt"
)
func main() {
m := map[string]string{"apple": "りんご", "peach": "もも"}
// 参照
a := m["apple"]
fmt.Println(a) // 出力用
// 更新
m["apple"] = "林檎"
fmt.Println(m) // 出力用
// 追加
m["pear"] = "なし"
fmt.Println(m) // 出力用
// 削除
delete(m, "pear")
fmt.Println(m) // 出力用
}
⚓V言語
参考: v/docs.md at master · vlang/v
- 名称: map
- おそらく現時点は文字列のみをキーにできる
// リテラルによる生成
mut m := {
'apple': "りんご"
'peach': "もも"
}
// 参考: 定義(空のmap)
mut m := map[string]int
- 実行可能なサンプルコード
mut m := {
'apple': "りんご"
'peach': "もも"
}
// 参照
a := m['apple']
println(a) //出力用
// 更新
m['apple'] = "林檎"
println(m) // 出力用
// 追加
m['pear'] = "なし"
println(m) // 出力用
// 削除
m.delete('pear')
println(m) // 出力用
⚓Swift
参考: 配列と辞書(連想配列) - Swift開発者Wiki
参考: Swiftの配列とハッシュ(連想配列) - Qiita
配列と連想配列がほぼ同じスタイル。
- 名称: 辞書(連想配列)
nil
を代入することで削除する点が特徴。
// リテラルによる生成
var d = [ "apple":"りんご", "peach":"もも" ]
- 実行可能なサンプルコード
var d = [ "apple":"りんご", "peach":"もも" ]
// 参照
var a = d["apple"]
print(a)
// 更新
d["apple"] = "林檎"
print(d) // 出力用
// 追加
d["pear"] = "なし"
print(d) // 出力用
// 削除
d["pear"] = nil
print(d) // 出力用
⚓PHP
参考: PHP: 配列 - Manual
PHPでは配列と連想配列の区別がないそうです。
- 名称: 配列(配列の要素に名前を付ける機能)
- 生成用リテラルに
[]
を使っている- PHP 5.4までは
[]
によるリテラル記法はなかった
- PHP 5.4までは
# リテラルによる生成
$a = ['apple' => 'りんご', 'peach' => 'もも'];
# 参考: 昔のarray()による生成
$a = array('apple' => 'りんご', 'peach' => 'もも');
- 実行可能なサンプルコード
<?php
$a = ['apple' => 'りんご', 'peach' => 'もも'];
// 参照
$b = $a['apple'];
var_dump($b); // 出力用
// 更新
$a['apple'] = "林檎";
var_dump($a); // 出力用
// 追加
$a['pear'] = "なし";
var_dump($a); // 出力用
// 削除
unset($a['pear']);
var_dump($a); // 出力用
?>
⚓Groovy
参考: 8. マップ - Apache Groovyチュートリアル
- 名称: map
Map map = [apple:"りんご", peach:"もも"]
- 実行可能なサンプルコード
class Code {
static void main(String[] args) {
Map m = [apple:"りんご", peach:"もも"]
println(m); // 出力用
// 参照
println(m.apple); // 出力用
// 更新
m.apple = "林檎";
println(m); // 出力用
// 追加
m["pear"] = "なし";
println(m); // 出力用
// 削除
m.remove("pear");
println(m); // 出力用
}
}
⚓Lua
参考: Lua テーブル(Table)
- 名称: テーブル(配列と同じ扱い)
- JavaScriptのようにフィールド名をドット
.
でも指定できる nil
を代入することで削除するのが特徴
table = { apple="りんご", peach="もも" }
- 実行可能なサンプルコード
table = { apple="りんご", peach="もも" }
-- 参照1
table = { apple="りんご", peach="もも" }
-- 参照1
t = table["apple"]
print(t) -- 出力用
-- 参照2
t = table.apple
print(t) -- 出力用
-- 更新
table["apple"] = "林檎"
for key, val in pairs( table ) do -- 出力用
print ( key, val )
end
-- 追加
table["pear"] = "なし"
for key, val in pairs( table ) do -- 出力用
print ( key, val )
end
-- 削除
table["pear"] = nil
for key, val in pairs( table ) do -- 出力用
print ( key, val )
end
⚓専用のリテラル記法のない言語
⚓Java
参考: 【HashMap】Javaで連想配列を扱う!サンプルつき
専用のリテラル構文はありません(HashMap
などのライブラリを用いる)。ここではHashMap
のみをチェック。
- 名称: HashMap
import java.util.HashMap;
public class Main {
public static void main(String[] args) {
HashMap<String, String> map;
{
map = new HashMap<String, String>();
// 追加
map.put("apple", "りんご");
map.put("peach", "もも");
// 参照
System.out.println(map.get("apple"));
// 更新
map.put("apple", "林檎");
System.out.println(map.get("apple")); // 出力用
// 追加
map.put("pear", "なし");
System.out.println(map.get("pear")); // 出力用
// 削除
map.remove("pear", "なし");
System.out.println(map.get("pear")); // 出力用
}
}
}
⚓Kotlin
気になってKotlinも調べたら、[]
というindexing operator構文が追加されていましたが、mapの生成リテラルはないようです。ここではhashMapOf()
のみ調べています。
- 名称: map(mutableとreadOnly)
[]
でのアクセスが推奨get()
、put()
、set()
なども使えるが非推奨
fun main() {
// hashMapOfによる生成
val id = "apple"
val name = "りんご"
val hashmap = hashMapOf(id to name)
println(hashmap) // 出力用
// 追加
hashmap["peach"] = "もも"
println(hashmap) // 出力用
// 更新
hashmap["apple"] = "林檎"
println(hashmap) // 出力用
// 追加
hashmap["pear"] = "なし"
println(hashmap) // 出力用
// 削除
hashmap.remove("pear")
println(hashmap) // 出力用
}
⚓C sharp
参考: ハッシュテーブル(連想配列)を使うには?[C#/VB、.NET 全バージョン]:.NET TIPS - @IT
- 名称: ハッシュテーブル
using System;
using System.Collections;
public class test {
static void Main() {
Hashtable ht = new Hashtable();
// 追加
ht["apple"] = "りんご";
ht.Add("peach","もも"); // Add()でも追加できる
foreach (DictionaryEntry de in ht) { // 出力用
Console.WriteLine("{0} : {1}", de.Key, de.Value);
}
// 更新
ht["apple"] = "林檎";
foreach (DictionaryEntry de in ht) { // 出力用
Console.WriteLine("{0} : {1}", de.Key, de.Value);
}
// 追加
ht["pear"] = "なし";
foreach (DictionaryEntry de in ht) { // 出力用
Console.WriteLine("{0} : {1}", de.Key, de.Value);
}
// 削除
ht.Remove("pear");
foreach (DictionaryEntry de in ht) { // 出力用
Console.WriteLine("{0} : {1}", de.Key, de.Value);
}
}
}
追記(2020/08/03): 以下の情報ありがとうございます🙇。
参考: Dictionary<TKey,TValue> クラス (System.Collections.Generic) | Microsoft Docs
⚓Rust
参考: ハッシュマップ - The Rust Programming Language
専用の構文はなく、HashMap
ライブラリを用います。
- 名称: HashMap
- ハッシュのアルゴリズムを変更できる
- 値の更新に
insert()
を使うのが特徴
use std::collections::HashMap;
fn main() {
// HashMap::new()による生成
let mut map: HashMap<String, String> = HashMap::new();
// 追加
map.insert(String::from("apple"), String::from("りんご"));
map.insert(String::from("peach"), String::from("もも"));
println!("{:?}", map); // 出力用
// 参照
let apple = String::from("apple");
let name = map.get(&apple);
println!("{:?}", name); // 出力用
// 更新
map.insert(String::from("apple"), String::from("林檎"));
println!("{:?}", map); // 出力用
// 追加(entry()とor_insert()を使用する場合)
map.entry(String::from("pear")).or_insert(String::from("なし"));
println!("{:?}", map); // 出力用
// 削除
map.remove("pear");
println!("{:?}", map); // 出力用
}
追記(2020/08/03): もっといい書き方があるそうです。ありがとうございます!
⚓Scala
参考: Scala Mapメモ(Hishidama's Scala Map Memo)
Scalaをどちらに分類するか迷いましたが、連想配列のためだけのリテラルというわけではなさそうだったのでここに置きました。
- 名称: Map(連想配列)
->
はタプルを作るメソッド+
で更新または追加、-
で削除する点が特徴
// リテラルによる生成
var a = Map[String, String]( "apple"->"りんご", "peach"->"もも" )
- 実行可能なサンプルコード
object Main {
def main(args: Array[String]): Unit = {
var a = Map[String, String]( "apple"->"りんご", "peach"->"もも" )
// 参照
var d = a("apple")
println(a); // 出力用
// 更新
a = a + ("apple"->"林檎");
println(a) // 出力用
// 追加
a = a + ("pear"->"なし");
println(a); // 出力用
// 削除
a = a - ("pear")
println(a); // 出力用
}
}
⚓Julia
参考: 実例で学ぶJuliaプログラミング言語入門
参考: Learn Julia in Y Minutes
参考: Collections and Data Structures · The Julia Language
参考: Julia入門 辞書(ハッシュテーブル)、Set型について - 0x00 nullbyte blog
- 名称: Dict(Associative Collection)
d = Dict("apple"=>"りんご", "peach"=>"もも")
# 型も指定できる
d = Dict{String, String}("apple"=>"りんご", "peach"=>"もも")
# 以前は以下のリテラルが使えたが、0.4以降でdeprecateされた
d = { "apple"=>"りんご", "peach"=>"もも" }
d = [ "apple"=>"りんご", "peach"=>"もも" ]
- 実行可能なサンプルコード
d = Dict("apple"=>"りんご", "peach"=>"もも")
# 参照
println(d["apple"])
# 更新
d["apple"] = "林檎"
println(d)
# 追加
d["pear"] = "なし"
println(d)
# 削除
delete!(d, "pear")
println(d)
⚓OCaml
参考: Hashtbl
- 名称: ハッシュテーブル
let hash = Hashtbl.create 5;;
Hashtbl.add hash "apple" "りんご";;
Hashtbl.add hash "peach" "もも";;
- 実行可能なサンプルコード
let hash = Hashtbl.create 5;;
Hashtbl.add hash "apple" "りんご";;
Hashtbl.add hash "peach" "もも";;
(*参照*)
print_endline ((Hashtbl.find hash "apple"));; (*出力用*)
print_endline ((Hashtbl.find hash "peach"));; (*出力用*)
(*更新*)
Hashtbl.replace hash "apple" "林檎";;
Hashtbl.iter (fun x y -> Printf.printf "%s -> %s\n" x y) hash;; (*出力用*)
(*追加*)
Hashtbl.add hash "pear" "なし";;
Hashtbl.iter (fun x y -> Printf.printf "%s -> %s\n" x y) hash;; (*出力用*)
(*削除*)
Hashtbl.remove hash "pear";;
Hashtbl.iter (fun x y -> Printf.printf "%s -> %s\n" x y) hash;; (*出力用*)
⚓C言語/C++
参考: GLib のハッシュテーブルを使う | Linux 修験道
参考: C 言語のハッシュテーブル! | Linux 修験道
参考: C/C++ で使える Hashtable - いけむランド
参考: C++ ハッシュ連想配列クラス unordered_map 入門
専用の構文はなく、定番のライブラリもひとつというわけではなさそうです。長くなりそうなのでサンプルコードは作りませんでした。ネットではハッシュテーブルを手作りする記事が多く見られました。
- 名称: 「ハッシュテーブル」と呼ばれることが多い
- 手作り、またはライブラリ次第
と思ったら、C++11以降ではstd::unordered_map
が使えると教わりました。後で追加してみます。
参考: C++ ハッシュ連想配列クラス unordered_map 入門
追記(2020/08/03): C++でやってみた方がいらっしゃいました。ありがとうございます!
記事書いた https://t.co/EQpPKQQUND
— バンビちゃん@実際クソザコメンタル (@pink_bangbi) August 1, 2020
調べてみて
専用のリテラル構文を持つ言語では、{}
を生成用リテラルとし、[]
でアクセスするものが多く、記法としてはこれが一応多数派なのかなと思えます。Perlでは()
、PHPとSwiftでは[]
を生成用リテラルに用いていることを知りましたが、Swiftのような新しい言語でも[]
で生成しているのを見ると、それはそれで筋が通っていそうな気がしました。
キーバリューをつなぐ記号も、=>
、->
、:
、=
などさまざまでした。
ばらつきが最も大きいのは、キーバリューの削除方法のようです。メソッドで削除する言語、削除用キーワードを使う言語、nil
代入で削除する言語があり、語もさまざまです。
使う側としては、やはりRubyやPythonのようなシンプルな生成用リテラルがある方が断然楽です。RustのHashMapでキーバリューをいくつも書くのはかなり大変そう...
一方専用のリテラル構文を持たない言語は、おそらくパフォーマンス上最適なハッシュ関数を用途に応じて選びたいという要求から、あえてリテラルを固定しない傾向があるのかもしれないと思えました(あくまで推測です)。このタイプではScalaが比較的書きやすそうなリテラルだと思いました(個人の感想です)。
実際、以下のようにハッシュ関数にはいろいろなものがあり、特性もさまざまです。
Juliaが当初採用していた生成用リテラルを後にdeprecateしたというあたり、考えさせられました。
個人的には、TypeScriptのユーザー定義の辞書型が既存のJavaScriptとぶつからないよう割とうまく切り抜けていると思いました。
ざっくりまとめ
生成リテラル
{}
- Ruby、Crystal、Python、JavaScript、TypeScript、Dart、V言語、Lua
%{}
- Elixir
()
- Perl
[]
- Swift、PHP、Groovy
map[型]型{キーバリュー}
- Go
Map[型, 型](キーバリュー)
- Scala
キーとバリューをつなげる記号
=>
- Ruby、Crystal、Elixir、Perl、PHP、Julia
:
- Python、JavaScript、TypeScript、Dart、Go、V言語、Swift、Groovy
=
- Lua
->
- Scala
アクセス記号
[]
- Ruby、Crystal、Elixir、Python、JavaScript、TypeScript、Dart、Go、V言語、Swift、PHP、Groovy、Lua、Kotlin、C#、Julia
{}
- Perl
おたより発掘
SwiftのようなArrayにもMapにも [] を使う言語では空mapのリテラルも紹介してほしい。
» いろいろな言語の連想配列系リテラル記法を比較してみた|TechRacho(テックラッチョ)〜エンジニアの「?」を「!」に〜|BPS株式会社 https://t.co/kdyrLkIX1B
— Yukihiro Matsumoto (@yukihiro_matz) July 31, 2020