This module provides an Array type with deterministic memory usage not reliant on the GC, as an alternative to the built-in arrays.
This module provides a BinaryHeap (aka priority queue) adaptor that makes a binary heap out of any user-provided random-access range.
This module implements a generic doubly-linked list container. It can be used as a queue, dequeue or stack.
This module implements a red-black tree container.
This module implements a singly-linked list container. It can be used as a stack.
This module contains some common utilities used by containers.
Red-black tree code copyright (C) 2008- by Steven Schveighoffer. Other code copyright 2010- Andrei Alexandrescu. All rights reserved by the respective holders.
This module defines generic containers.
$(LNAME2 construction, Construction)
To implement the different containers, both struct and class based approaches have been used. std.container.util.make allows for uniform construction with either approach.
$(RUNNABLE_EXAMPLE --- import std.container; // Construct a red-black tree and an array both containing the values 1, 2, 3. // RedBlackTree should typically be allocated using `new` RedBlackTree!int rbTree = new RedBlackTree!int(1, 2, 3); // But `new` should not be used with Array Array!int array = Array!int(1, 2, 3); // `make` hides the differences RedBlackTree!int rbTree2 = make!(RedBlackTree!int)(1, 2, 3); Array!int array2 = make!(Array!int)(1, 2, 3); assert(rbTree == rbTree2); assert(array == array2); --- )
Note that make can infer the element type from the given arguments.
$(LNAME2 reference-semantics, Reference Semantics)
All containers have reference semantics, which means that after assignment both variables refer to the same underlying data.
To make a copy of a container, use the c.dup container primitive.
$(RUNNABLE_EXAMPLE --- import std.algorithm, std.container, std.range; Array!int originalArray = make!(Array!int)(1, 2, 3); Array!int secondArray = originalArray; assert(equal(originalArray[], secondArray[])); // changing one instance changes the other one as well! originalArray[0] = 12; assert(secondArray[0] == 12); // secondArray now refers to an independent copy of originalArray secondArray = originalArray.dup; secondArray[0] = 1; // assert that originalArray has not been affected assert(originalArray[0] == 12); --- )
Attention: If the container is implemented as a class, using an uninitialized instance can cause a null pointer dereference.
Using an uninitialized struct-based container will work, because the struct intializes itself upon use; however, up to this point the container will not have an identity and assignment does not create two references to the same data.
$(RUNNABLE_EXAMPLE --- import std.container; // create an uninitialized array Array!int array1; // array2 does _not_ refer to array1 Array!int array2 = array1; array2.insertBack(42); // thus array1 will not be affected assert(array1.empty); // after initialization reference semantics work as expected array1 = array2; // now affects array2 as well array1.removeBack(); assert(array2.empty); --- )
It is therefore recommended to always construct containers using std.container.util.make.
This is in fact necessary to put containers into another container. For example, to construct an Array of ten empty Arrays, use the following that calls make ten times.
$(LNAME2 submodules, Submodules)
This module consists of the following submodules:
$(LNAME2 primary-range, The Primary Range of a Container)
While some containers offer direct access to their elements e.g. via opIndex, c.front or c.back, access and modification of a container's contents is generally done through its primary range type, which is aliased as C.Range. For example, the primary range type of Array!int is Array!int.Range.
If the documentation of a member function of a container takes a parameter of type Range, then it refers to the primary range type of this container. Oftentimes Take!Range will be used, in which case the range refers to a span of the elements in the container. Arguments to these parameters must be obtained from the same container instance as the one being worked with. It is important to note that many generic range algorithms return the same range type as their input range.
$(RUNNABLE_EXAMPLE --- import std.algorithm.comparison : equal; import std.algorithm.searching : find; import std.container; import std.range : take; auto array = make!Array(1, 2, 3); // `find` returns an Array!int.Range advanced to the element "2" array.linearRemove(array[].find(2)); assert(array[].equal([1])); array = make!Array(1, 2, 3); // the range given to `linearRemove` is a Take!(Array!int.Range) // spanning just the element "2" array.linearRemove(array[].find(2).take(1)); assert(array[].equal([1, 3])); --- )
When any range can be passed as an argument to a member function, the documention usually refers to the parameter's templated type as Stuff.
$(RUNNABLE_EXAMPLE --- import std.algorithm.comparison : equal; import std.container; import std.range : iota; auto array = make!Array(1, 2); // the range type returned by `iota` is completely unrelated to Array, // which is fine for Array.insertBack: array.insertBack(iota(3, 10)); assert(array[].equal([1, 2, 3, 4, 5, 6, 7, 8, 9])); --- )
$(LNAME2 primitives, Container Primitives)
Containers do not form a class hierarchy, instead they implement a common set of primitives (see table below). These primitives each guarantee a specific worst case complexity and thus allow generic code to be written independently of the container implementation.
For example the primitives c.remove(r) and c.linearRemove(r) both remove the sequence of elements in range r from the container c. The primitive c.remove(r) guarantees O(nr log nc) complexity in the worst case and c.linearRemove(r) relaxes this guarantee to O(nc).
Since a sequence of elements can be removed from a doubly linked list in constant time, DList provides the primitive c.remove(r) as well as c.linearRemove(r). On the other hand Array only offers c.linearRemove(r).
The following table describes the common set of primitives that containers implement. A container need not implement all primitives, but if a primitive is implemented, it must support the syntax described in the syntax column with the semantics described in the description column, and it must not have a worst-case complexity worse than denoted in big-O notation in the O(·) column. Below, C means a container type, c is a value of container type, n$(SUBSCRIPT x) represents the effective length of value x, which could be a single element (in which case n$(SUBSCRIPT x) is 1), a container, or a range.