Movatterモバイル変換


[0]ホーム

URL:


D Logo
Menu
Search

Language Reference

table of contents

Report a bug
If you spot a problem with this page, click here to create a Bugzilla issue.
Improve this page
Quickly fork, edit online, and submit a pull request for this page.Requires a signed-in GitHub account. This works well for small changes.If you'd like to make larger changes you may want to consider usinga local clone.

Associative Arrays

Contents
  1. Literals
  2. Removing Keys
  3. Testing Membership
  4. Using Classes as the KeyType
  5. Using Structs or Unions as the KeyType
  6. Construction or Assignment on Setting AA Entries
  7. Inserting if not present
  8. Advanced updating
  9. Runtime Initialization of Immutable AAs
  10. Construction and Reference Semantics
  11. Properties and Operations
    1. Iteration Operations
    2. Key Lookup Operations
  12. Examples
    1. Associative Array Example: word count
    2. Associative Array Example: counting pairs

Associative arrays have an index that is not necessarily an integer, and can be sparsely populated. The index for an associative array is called thekey, and its type is called theKeyType.

Associative arrays are declared by placing theKeyType within the[ ] of an array declaration:

int[string] aa;// Associative array of ints that are// indexed by string keys.// The KeyType is string.aa["hello"] = 3;// set value associated with key "hello" to 3int value = aa["hello"];// lookup value from a keyassert(value == 3);

Neither theKeyTypes nor the element types of an associative array can be function types orvoid.

Implementation Defined: The built-in associative arrays do not preserve the order of the keys inserted into the array. In particular, in aforeach loop the order in which the elements are iterated is typically unspecified.

Literals

auto aa = [21u:"he", 38:"ho", 2:"hi"];staticassert(is(typeof(aa) == string[uint]));assert(aa[2] =="hi");

SeeAssociative Array Literals.

Removing Keys

Particular keys in an associative array can be removed with theremove function:

aa.remove("hello");

remove(key) does nothing if the givenkey does not exist and returnsfalse. If the givenkey does exist, it removes it from the AA and returnstrue.

All keys can be removed by using the methodclear.

Testing Membership

TheInExpression yields a pointer to the value if the key is in the associative array, ornull if not:

int* p;p ="hello"in aa;if (p !isnull){    *p = 4;// update value associated with keyassert(aa["hello"] == 4);}
Undefined Behavior: Adjusting the pointer to point before or after the element whose address is returned, and then dereferencing it.

Using Classes as the KeyType

Classes can be used as theKeyType. The behavior is controlled by the following member functions of classObject:

Note that the parameter toopEquals is of typeObject, not the type of the class in which it is defined.

For example:

class Foo{int a, b;override size_ttoHash() {return a + b; }overrideboolopEquals(Object o)    {        Foo foo =cast(Foo) o;return foo && a == foo.a && b == foo.b;    }}
The default implementation ofopEquals uses the address of the instance for comparisons, and the default implementation oftoHash hashes the address of the instance.
Implementation Defined:opCmp is not used to check for equality by the associative array. However, since the actualopEquals oropCmp called is not decided until runtime, the compiler cannot always detect mismatched functions. Because of legacy issues, the compiler may reject an associative array key type that overridesopCmp but notopEquals. This restriction may be removed in future versions.
Undefined Behavior:
  1. IftoHash must consistently be the same value whenopEquals returns true. In other words, two objects that are considered equal should always have the same hash value. Otherwise, undefined behavior will result.
Best Practices:
  1. Use the attributes@safe,@nogc,pure,const, andscope as much as possible on thetoHash andopEquals overrides.

Using Structs or Unions as the KeyType

If theKeyType is a struct or union type, a default mechanism is used to compute the hash and comparisons of it based on the fields of the struct value. A custom mechanism can be used by providing the following functions as struct members:

size_ttoHash()const @safepurenothrow;boolopEquals(refconsttypeof(this) s)const @safepurenothrow;

For example:

import std.string;struct MyString{    string str;    size_ttoHash()const @safepurenothrow    {        size_t hash;foreach (char c; str)            hash = (hash * 9) + c;return hash;    }boolopEquals(refconst MyString s)const @safepurenothrow    {return std.string.cmp(this.str, s.str) == 0;    }}

The functions can use@trusted instead of@safe.

Implementation Defined:opCmp is not used to check for equality by the associative array. For this reason, and for legacy reasons, an associative array key is not allowed to define a specializedopCmp, but omit a specializedopEquals. This restriction may be removed in future versions of D.
Undefined Behavior:
  1. IftoHash must consistently be the same value whenopEquals returns true. In other words, two structs that are considered equal should always have the same hash value. Otherwise, undefined behavior will result.
Best Practices:
  1. Use the attributes@nogc as much as possible on thetoHash andopEquals overrides.

Construction or Assignment on Setting AA Entries

When an AA indexing access appears on the left side of an assignment operator, it is specially handled for setting an AA entry associated with the key.

string[int] aa;string s;//s = aa[1];        // throws RangeError in runtimeaa[1] ="hello";// handled for setting AA entrys = aa[1];// succeeds to lookupassert(s =="hello");

If the assigned value type is equivalent with the AA element type:

  1. If the indexing key does not yet exist in AA, a new AA entry will be allocated, and it will be initialized with the assigned value.
  2. If the indexing key already exists in the AA, the setting runs normal assignment.
struct S{int val;void opAssign(S rhs) {this.val = rhs.val * 2; }}S[int] aa;aa[1] = S(10);// first setting initializes the entry aa[1]assert(aa[1].val == 10);aa[1] = S(10);// second setting invokes normal assignment, and// operator-overloading rewrites it to member opAssign function.assert(aa[1].val == 20);

If the assigned value type isnot equivalent with the AA element type, the expression could invoke operator overloading with normal indexing access:

struct S{int val;void opAssign(int v) {this.val = v * 2; }}S[int] aa;aa[1] = 10;// is rewritten to: aa[1].opAssign(10), and// throws RangeError before opAssign is called

However, if the AA element type is a struct which supports an implicit constructor call from the assigned value, implicit construction is used for setting the AA entry:

struct S{int val;this(int v) {this.val = v; }void opAssign(int v) {this.val = v * 2; }}S s = 1;// OK, rewritten to: S s = S(1);s = 1;// OK, rewritten to: s.opAssign(1);S[int] aa;aa[1] = 10;// first setting is rewritten to: aa[1] = S(10);assert(aa[1].val == 10);aa[1] = 10;// second setting is rewritten to: aa[1].opAssign(10);assert(aa[1].val == 20);
This is designed for efficient memory reuse with some value-semantics structs, eg.std.bigint.BigInt.
import std.bigint;BigInt[string] aa;aa["a"] = 10;// construct BigInt(10) and move it in AAaa["a"] = 20;// call aa["a"].opAssign(20)

Inserting if not present

When AA access requires that there must be a value corresponding to the key, a value must be constructed and inserted if not present. Therequire function provides a means to construct a new value via a lazy argument. The lazy argument is evaluated when the key is not present. Therequire operation avoids the need to perform multiple key lookups.

class C{}C[string] aa;auto a = aa.require("a",new C);// lookup "a", construct if not present

Sometimes it is necessary to know whether the value was constructed or already exists. Therequire function doesn't provide a boolean parameter to indicate whether the value was constructed but instead allows the construction via a function or delegate. This allows the use of any mechanism as demonstrated below.

class C{}C[string] aa;bool constructed;auto a = aa.require("a", { constructed=true;returnnew C;}());assert(constructed ==true);C newc;auto b = aa.require("b", { newc =new C;return newc;}());assert(bis newc);

Advanced updating

Typically updating a value in an associative array is simply done with an assign statement.

int[string] aa;aa["a"] = 3;// set value associated with key "a" to 3

Sometimes it is necessary to perform different operations depending on whether a value already exists or needs to be constructed. Theupdate function provides a means to construct a new value via thecreator or update an existing value via theupdater. Theupdate operation avoids the need to perform multiple key lookups.

int[string] aa;// createaa.update("key",    () => 1,    (int) {}// not executed    );assert(aa["key"] == 1);// update value by refaa.update("key",    () => 0,// not executed    (refint v) {        v += 1;    });assert(aa["key"] == 2);

For details, seeupdate.

Runtime Initialization of Immutable AAs

Immutable associative arrays are often desirable, but sometimes initialization must be done at runtime. This can be achieved with a constructor (static constructor depending on scope), a buffer associative array andassumeUnique:

immutablelong[string] aa;sharedstaticthis(){import std.exception : assumeUnique;import std.conv : to;long[string] temp;// mutable bufferforeach (i; 0 .. 10)    {        temp[to!string(i)] = i;    }    temp.rehash;// for faster lookups    aa = assumeUnique(temp);}void main(){assert(aa["1"] == 1);assert(aa["5"] == 5);assert(aa["9"] == 9);}

Construction and Reference Semantics

An Associative Array defaults tonull, and is constructed upon assigning the first key/value pair. However, once constructed, an associative array hasreference semantics, meaning that assigning one array to another does not copy the data. This is especially important when attempting to create multiple references to the same array.

int[int] aa;// defaults to nullint[int] aa2 = aa;// copies the null referenceassert(aaisnull);aa[1] = 1;assert(aa2.length == 0);// aa2 still is nullaa2 = aa;aa2[2] = 2;assert(aa[2] == 2);// now both refer to the same instance

ANewExpression allows constructing an associative array instance immediately, rather than wheninserting a key.

int[string] a =newint[string];auto b = a;// a and b point to the same AA instanceassert(b !isnull);a["1"] = 1;assert(b["1"] == 1);

Properties and Operations

NameDescription
sizeofThe size of the reference to the associative array; it is 4 in 32-bit builds and 8 on 64-bit builds.
lengthThe number of values in the associative array. Unlike for dynamic arrays, it is read-only.
dupReturnsnull if the associative array isnull; otherwise returns a newly allocated associative array with copies of the keys and values of the associative array.
rehashReorganizes the associative array in place so that lookups are more efficient. Callingrehash is effective when, for example, the program is done loading up a symbol table and now needs fast lookups in it. Returns a reference to the reorganized array.
clearRemoves all keys and values from an associative array. The array is not rehashed after removal to allow for the existing storage to be reused. This will affect all references to the same instance and is not equivalent todestroy(aa) which only sets the current reference tonull.
Note: There is no built-inempty property. Phobos provides an implementation ofempty instd.range.primitives.

Iteration Operations

OperationDescription
keysReturns a newly allocated dynamic array containing copies of the keys in the associative array. The order is consistent withvalues() but otherwise unspecified.
valuesReturns a newly allocated dynamic array containing copies of the values in the associative array. The order is consistent withkeys() but otherwise unspecified.
byKeyReturns aforward range enumerating the keys by reference. The order is consistent withbyValue() but otherwise unspecified.
Bug: The keys are provided as mutable, but mutating them is undefined behavior.
byValueReturns a forward range enumerating the values by reference. The order is consistent withbyKey() but otherwise unspecified.
byKeyValueReturns a forward range enumerating opaque objects that providekey andvalue properties. These properties return their result by reference. The order of elements is unspecified.
Bug: The keys are provided as mutable, but mutating them is undefined behavior.

The order of keys and values returned bykeys() andvalues() as well asbyKey(),byValue(), andbyKeyValue() is unspecified, but is guaranteed to be consistent as long as the associative array has not been reorganized, e.g. by adding or removing keys between the calls. Associating a new value to an existing key does not reorganize an associative array. Reorganizing an associative array invalidates any input ranges returned bybyKey(),byValue(), andbyKeyValue().

Best Practices:
  • Callingkeys() andvalues() incurs an allocation (unless the associative array isnull or empty). Use them if you need an independent copy of the keys and/or values; otherwise considerbyKey() andbyValue().
  • Associative arrays supportforeach directly for value iteration and key-value iteration. Useref on the key and value variables when necessary to avoid unnecessary copies. For iterating the keys only, use key-value iteration and ignore the value. UsebyKey(),byValue(), orbyKeyValue() for more elaborate cases such asrange algorithms.

Key Lookup Operations

OperationDescription
Valueget(Key key, lazy Value defVal) If the key exists, returns corresponding value; otherwise evaluates and returnsdefVal without associating it withkey.
ref Valuerequire(Key key, lazy Value value) If the key exists, returns corresponding value by reference; otherwise evaluatesvalue and associates it withkey in the associative array, then returns the newly stored value by reference.
voidupdate(Key key, Creator creator, Updater updater) If the key exists, it callsupdater with the corresponding value; if it returns a value, it associates the value with the key. If the key was not found, it invokescreator and associates the result with the key.

Theupdate operation works with anycreator andupdater that is invokable as specified.updater can modify the value in-place if it binds its argument by reference.

Best Practices: Avoid returningupdater with aref parameter avoids unnecessary copies.

Examples

Associative Array Example: word count

too many cookstoo many ingredients
import std.algorithm;import std.stdio;void main(){ulong[string] dictionary;ulong wordCount, lineCount, charCount;foreach (line; stdin.byLine(KeepTerminator.yes))    {        charCount += line.length;foreach (word; splitter(line))        {            wordCount += 1;if (auto count = wordin dictionary)                *count += 1;else                dictionary[word.idup] = 1;        }        lineCount += 1;    }    writeln("   lines   words   bytes");    writefln("%8s%8s%8s", lineCount, wordCount, charCount);constchar[37] hr = '-';    writeln(hr);foreach (word; sort(dictionary.keys))    {        writefln("%3s %s", dictionary[word], word);    }}

Seewc for the full version.

Associative Array Example: counting pairs

An Associative Array can be iterated in key/value fashion using aforeach statement. As an example, the number of occurrences of all possible substrings of length 2 (aka 2-mers) in a string will be counted:

import std.range : slide;import std.stdio : writefln;import std.utf : byCodeUnit;// avoids UTF-8 auto-decodingint[string] aa;// The string `arr` has a limited alphabet: {A, C, G, T}// Thus, for better performance, iteration can be done _without_ decodingauto arr ="AGATAGA".byCodeUnit;// iterate over all pairs in the string and count each pair// ('A', 'G'), ('G', 'A'), ('A', 'T'), ...foreach (window; arr.slide(2))    aa[window.source]++;// source unwraps the code unit range// iterate over all key/value pairs of the Associative Arrayforeach (key, value; aa){    writefln("key: %s, value: %d", key, value);}
> rdmd count.dkey: AT, value: 1key: GA, value: 2key: TA, value: 1key: AG, value: 2
Arrays
Structs and Unions
Copyright © 1999-2026 by theD Language Foundation | Page generated byDdoc on Sat Feb 21 04:14:53 2026

[8]ページ先頭

©2009-2026 Movatter.jp