copyright 2010- Andrei Alexandrescu. All rights reserved by the respective holders.
License
(See accompanying file LICENSE_1_0.txt or copy at boost.org/LICENSE_1_0.txt).
This module defines generic containers.
ConstructionTo implement the different containers, both struct and class based approaches have been used. make allows for uniform construction with either approach.
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.
import std.container;
auto rbTree = make!RedBlackTree(1, 2, 3); // RedBlackTree!int
auto array = make!Array("1", "2", "3"); // Array!stringReference 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.
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.
import std.container;
RedBlackTree!int rbTree;
rbTree.insert(5); // null pointer dereferenceUsing 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.
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
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.
import std.container, std.range;
auto arrOfArrs = make!Array(generate!(() => make!(Array!int)).take(10));Submodules
This module consists of the following submodules:
std.container.array module provides
an array type with deterministic control of memory, not reliant on the GC unlike built-in arrays.
std.container.binaryheap module
provides a binary heap implementation that can be applied to any user-provided random-access range.
std.container.dlist module provides
a doubly-linked list implementation.
std.container.rbtree module
implements red-black trees.
std.container.slist module
implements singly-linked lists.
std.container.util module contains
some generic tools commonly used by container implementations.
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.
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.
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]));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 nr log nc complexity in the worst case and c.linearRemove(r) relaxes this guarantee to 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
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 · column. Below, C means a container type, c is a value of container type, nx represents the effective length of value x, which could be a single element (in which case nx is 1), a container, or a range.
Source: std/container/package.d
copyright 2010- Andrei Alexandrescu. All rights reserved by the respective holders.
(See accompanying file LICENSE_1_0.txt or copy at boost.org/LICENSE_1_0.txt).
TotalContainer dup() @propertyReturns a duplicate of the container. The elements themselves are not transitively duplicated.size_t capacity() @propertyReturns the maximum number of elements the container can store without (a) allocating memory, (b) invalidating iterators upon insertion.void reserve(size_t e)Ensures sufficient capacity to accommodate `n` elements.Range opSlice()Returns a range that iterates over all elements of the container, in a container-defined order. The container should choose the most convenient and fast method of iteration for `opSlice()`.Range opSlice(size_t a, size_t b)Returns a range that iterates the container between two specified positions.T moveFront()DittoT moveBack()Dittovoid opIndexAssign(KeyType i, T value)dittoT opIndexUnary(string op)(KeyType i)dittovoid opIndexOpAssign(string op)(KeyType i, T value)dittobool opBinaryRight(string op)(KeyType k) if (op == "in")k in container returns true if the given key is in the container.Range equalRange(KeyType k)Returns a range of all elements containing `k` (could be empty or a singleton range).Range lowerBound(KeyType k)Returns a range of all elements with keys less than `k` (could be empty or a singleton range). Only defined by containers that store data sorted at all times.Range upperBound(KeyType k)Returns a range of all elements with keys larger than `k` (could be empty or a singleton range). Only defined by containers that store data sorted at all times.TotalContainer opBinary(string op)(Stuff rhs) if (op == "~")Returns a new container that's the concatenation of `this` and its argument. `opBinaryRight` is only defined if `Stuff` does not define `opBinary`.TotalContainer opBinaryRight(string op)(Stuff lhs) if (op == "~")dittovoid opOpAssign(string op)(Stuff stuff) if (op == "~")Forwards to insertAfter(this[]).void clear()Removes all contents from the container. The container decides how capacity is affected.void length(size_t newLength) @propertySets the number of elements in the container to `newSize`. If newSize is greater than `length`, the added elements are added to unspecified positions in the container and initialized with .init.size_t insert(Stuff)(Stuff stuff)Inserts `stuff` in an unspecified position in the container. Implementations should choose whichever insertion means is the most advantageous for the container, but document the exact behavior. `st...size_t stableInsert(Stuff)(Stuff stuff)dittosize_t linearInsert(Stuff)(Stuff stuff)Same as `insert(stuff)` and `stableInsert(stuff)` respectively, but relax the complexity constraint to linear.size_t stableLinearInsert(Stuff)(Stuff stuff)dittoT removeAny()Picks one value in an unspecified position in the container, removes it from the container, and returns it. Implementations should pick the value that's the most advantageous for the container. The...T stableRemoveAny()dittosize_t insertFront(Stuff)(Stuff stuff)Inserts `value` to the front or back of the container. `stuff` can be a value convertible to the container's element type or a range of values convertible to it. The stable version behaves the same...size_t stableInsertFront(Stuff)(Stuff stuff)dittosize_t insertBack(Stuff)(Stuff stuff)dittosize_t stableInsertBack(T value)dittovoid removeFront()Removes the value at the front or back of the container. The stable version behaves the same, but guarantees that ranges iterating over the container are never invalidated. The optional parameter h...void stableRemoveFront()dittovoid removeBack()dittovoid stableRemoveBack()dittosize_t removeFront(size_t howMany)Removes `howMany` values at the front or back of the container. Unlike the unparameterized versions above, these functions do not throw if they could not remove `howMany` elements. Instead, if howM...size_t stableRemoveFront(size_t howMany)dittosize_t removeBack(size_t howMany)dittosize_t stableRemoveBack(size_t howMany)dittosize_t insertBefore(Stuff)(Range r, Stuff stuff)Inserts `stuff` before, after, or instead range `r`, which must be a valid range previously extracted from this container. `stuff` can be a value convertible to the container's element type or a ra...size_t stableInsertBefore(Stuff)(Range r, Stuff stuff)dittosize_t insertAfter(Stuff)(Range r, Stuff stuff)dittosize_t stableInsertAfter(Stuff)(Range r, Stuff stuff)dittosize_t stableReplace(Stuff)(Range r, Stuff stuff)dittoRange remove(Range r)Removes all elements belonging to `r`, which must be a range obtained originally from this container. The stable version behaves the same, but guarantees that ranges iterating over the container ar...Range stableRemove(Range r)dittoRange linearRemove(Range r)Same as `remove` above, but has complexity relaxed to linear.Range stableLinearRemove(Range r)dittoRangeDefines the container's primary range, which embodies one of the ranges defined in std.