| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174 | /******************************************************************************* copyright: Copyright (c) 2008 Kris Bell. All rights reserved license: BSD style: $(LICENSE) version: Apr 2008: Initial release authors: Kris Since: 0.99.7 Based upon Doug Lea's Java collection package *******************************************************************************/ module tango.util.container.LinkedList; private import tango.util.container.Slink; public import tango.util.container.Container; private import tango.util.container.model.IContainer; /******************************************************************************* List of singly-linked values --- Iterator iterator () int opApply (scope int delegate(ref V value) dg) V head () V tail () V head (V value) V tail (V value) V removeHead () V removeTail () bool contains (V value) size_t first (V value, size_t startingIndex = 0) size_t last (V value, size_t startingIndex = 0) LinkedList add (V value) LinkedList prepend (V value) size_t prepend (IContainer!(V) e) LinkedList append (V value) size_t append (IContainer!(V) e) LinkedList addAt (size_t index, V value) size_t addAt (size_t index, IContainer!(V) e) V get (size_t index) bool take (ref V v) size_t remove (V value, bool all) bool removeAt (size_t index) size_t removeRange (size_t fromIndex, size_t toIndex) size_t replace (V oldElement, V newElement, bool all) bool replaceAt (size_t index, V value) LinkedList clear () LinkedList reset () LinkedList subset (size_t from, size_t length = size_t.max) LinkedList dup () size_t size () bool isEmpty () V[] toArray (V[] dst) LinkedList sort (Compare!(V) cmp) LinkedList check () --- *******************************************************************************/ class LinkedList (V, alias Reap = Container.reap, alias Heap = Container.DefaultCollect) : IContainer!(V) { // use this type for Allocator configuration private alias Slink!(V) Type; private alias Type* Ref; private alias V* VRef; private alias Heap!(Type) Alloc; // number of elements contained private size_t count; // configured heap manager private Alloc heap; // mutation tag updates on each change private size_t mutation; // head of the list. Null if empty private Ref list; /*********************************************************************** Create a new empty list ***********************************************************************/ this () { this (null, 0); } /*********************************************************************** Special version of constructor needed by dup ***********************************************************************/ protected this (Ref l, size_t c) { list = l; count = c; } /*********************************************************************** Clean up when deleted ***********************************************************************/ ~this () { reset; } /*********************************************************************** Return a generic iterator for contained elements ***********************************************************************/ final Iterator iterator () { Iterator i = void; i.mutation = mutation; i.node = list ? *(i.hook = &list) : null; i.prior = null; i.owner = this; return i; } /*********************************************************************** Configure the assigned allocator with the size of each allocation block (number of nodes allocated at one time) and the number of nodes to pre-populate the cache with. Time complexity: O(n) ***********************************************************************/ final LinkedList cache (size_t chunk, size_t count=0) { heap.config (chunk, count); return this; } /*********************************************************************** ***********************************************************************/ final int opApply (scope int delegate(ref V value) dg) { return iterator.opApply (dg); } /*********************************************************************** Return the number of elements contained ***********************************************************************/ @property final const size_t size () { return count; } /*********************************************************************** Build an independent copy of the list. The elements themselves are not cloned ***********************************************************************/ @property final LinkedList dup () { return new LinkedList!(V, Reap, Heap) (list ? list.copy(&heap.allocate) : null, count); } /*********************************************************************** Time complexity: O(n) ***********************************************************************/ final bool contains (V value) { if (count is 0) return false; return list.find(value) !is null; } /*********************************************************************** Time complexity: O(1) ***********************************************************************/ final V head () { return firstCell.value; } /*********************************************************************** Time complexity: O(n) ***********************************************************************/ final V tail () { return lastCell.value; } /*********************************************************************** Time complexity: O(n) ***********************************************************************/ final V get (size_t index) { return cellAt(index).value; } /*********************************************************************** Time complexity: O(n) Returns size_t.max if no element found. ***********************************************************************/ final size_t first (V value, size_t startingIndex = 0) { if (list is null || startingIndex >= count) return size_t.max; if (startingIndex < 0) startingIndex = 0; auto p = list.nth (startingIndex); if (p) { auto i = p.index (value); if (i >= 0) return i + startingIndex; } return size_t.max; } /*********************************************************************** Time complexity: O(n) Returns size_t.max if no element found. ***********************************************************************/ final size_t last (V value, size_t startingIndex = 0) { if (list is null) return size_t.max; auto i = 0; if (startingIndex >= count) startingIndex = count - 1; auto index = size_t.max; auto p = list; while (i <= startingIndex && p) { if (p.value == value) index = i; ++i; p = p.next; } return index; } /*********************************************************************** Time complexity: O(length) ***********************************************************************/ final LinkedList subset (size_t from, size_t length = size_t.max) { Ref newlist = null; if (length > 0) { auto p = cellAt (from); auto current = newlist = heap.allocate.set (p.value, null); for (auto i = 1; i < length; ++i) if ((p = p.next) is null) length = i; else { current.attach (heap.allocate.set (p.value, null)); current = current.next; } } return new LinkedList (newlist, length); } /*********************************************************************** Time complexity: O(n) ***********************************************************************/ final LinkedList clear () { return clear (false); } /*********************************************************************** Reset the HashMap contents and optionally configure a new heap manager. We cannot guarantee to clean up reconfigured allocators, so be sure to invoke reset() before discarding this class Time complexity: O(n) ***********************************************************************/ final LinkedList reset () { return clear (true); } /*********************************************************************** Takes the first value on the list Time complexity: O(1) ***********************************************************************/ final bool take (ref V v) { if (count) { v = head; removeHead; return true; } return false; } /*********************************************************************** Uses a merge-sort-based algorithm. Time complexity: O(n log n) ***********************************************************************/ final LinkedList sort (Compare!(V) cmp) { if (list) { list = Ref.sort (list, cmp); mutate; } return this; } /*********************************************************************** Time complexity: O(1) ***********************************************************************/ final LinkedList add (V value) { return prepend (value); } /*********************************************************************** Time complexity: O(1) ***********************************************************************/ final LinkedList prepend (V value) { list = heap.allocate.set (value, list); increment; return this; } /*********************************************************************** Time complexity: O(n) ***********************************************************************/ final size_t remove (V value, bool all = false) { auto c = count; if (c) { auto p = list; auto trail = p; while (p) { auto n = p.next; if (p.value == value) { decrement (p); if (p is list) { list = n; trail = n; } else trail.next = n; if (!all || count is 0) break; else p = n; } else { trail = p; p = n; } } } return c - count; } /*********************************************************************** Time complexity: O(n) ***********************************************************************/ final size_t replace (V oldElement, V newElement, bool all = false) { size_t c; if (count && oldElement != newElement) { auto p = list.find (oldElement); while (p) { ++c; mutate; p.value = newElement; if (!all) break; p = p.find (oldElement); } } return c; } /*********************************************************************** Time complexity: O(1) ***********************************************************************/ final V head (V value) { auto cell = firstCell; auto v = cell.value; cell.value = value; mutate; return v; } /*********************************************************************** Time complexity: O(1) ***********************************************************************/ final V removeHead () { auto p = firstCell; auto v = p.value; list = p.next; decrement (p); return v; } /*********************************************************************** Time complexity: O(n) ***********************************************************************/ final LinkedList append (V value) { if (list is null) prepend (value); else { list.tail.next = heap.allocate.set (value, null); increment; } return this; } /*********************************************************************** Time complexity: O(n) ***********************************************************************/ final V tail (V value) { auto p = lastCell; auto v = p.value; p.value = value; mutate; return v; } /*********************************************************************** Time complexity: O(n) ***********************************************************************/ final V removeTail () { if (firstCell.next is null) return removeHead; auto trail = list; auto p = trail.next; while (p.next) { trail = p; p = p.next; } trail.next = null; auto v = p.value; decrement (p); return v; } /*********************************************************************** Time complexity: O(n) ***********************************************************************/ final LinkedList addAt (size_t index, V value) { if (index is 0) prepend (value); else { cellAt(index - 1).attach (heap.allocate.set(value, null)); increment; } return this; } /*********************************************************************** Time complexity: O(n) ***********************************************************************/ final LinkedList removeAt (size_t index) { if (index is 0) removeHead; else { auto p = cellAt (index - 1); auto t = p.next; p.detachNext; decrement (t); } return this; } /*********************************************************************** Time complexity: O(n) ***********************************************************************/ final LinkedList replaceAt (size_t index, V value) { cellAt(index).value = value; mutate; return this; } /*********************************************************************** Time complexity: O(number of elements in e) ***********************************************************************/ final size_t prepend (IContainer!(V) e) { auto c = count; splice_ (e, null, list); return count - c; } /*********************************************************************** Time complexity: O(n + number of elements in e) ***********************************************************************/ final size_t append (IContainer!(V) e) { auto c = count; if (list is null) splice_ (e, null, null); else splice_ (e, list.tail, null); return count - c; } /*********************************************************************** Time complexity: O(n + number of elements in e) ***********************************************************************/ final size_t addAt (size_t index, IContainer!(V) e) { auto c = count; if (index is 0) splice_ (e, null, list); else { auto p = cellAt (index - 1); splice_ (e, p, p.next); } return count - c; } /*********************************************************************** Time complexity: O(n) ***********************************************************************/ final size_t removeRange (size_t fromIndex, size_t toIndex) { auto c = count; if (fromIndex <= toIndex) { if (fromIndex is 0) { auto p = firstCell; for (size_t i = fromIndex; i <= toIndex; ++i) p = p.next; list = p; } else { auto f = cellAt (fromIndex - 1); auto p = f; for (size_t i = fromIndex; i <= toIndex; ++i) p = p.next; f.next = p.next; } count -= (toIndex - fromIndex + 1); mutate; } return c - count; } /*********************************************************************** Copy and return the contained set of values in an array, using the optional dst as a recipient (which is resized as necessary). Returns a slice of dst representing the container values. Time complexity: O(n) ***********************************************************************/ final V[] toArray (V[] dst = null) { if (dst.length < count) dst.length = count; size_t i = 0; foreach (v; this) dst[i++] = v; return dst [0 .. count]; } /*********************************************************************** Is this container empty? Time complexity: O(1) ***********************************************************************/ final const bool isEmpty () { return count is 0; } /*********************************************************************** ***********************************************************************/ final LinkedList check () { assert(((count is 0) is (list is null))); assert((list is null || list.count is size)); size_t c = 0; for (Ref p = list; p; p = p.next) { assert(instances(p.value) > 0); assert(contains(p.value)); ++c; } assert(c is count); return this; } /*********************************************************************** Time complexity: O(n) ***********************************************************************/ private size_t instances (V value) { if (count is 0) return 0; return list.count (value); } /*********************************************************************** ***********************************************************************/ private Ref firstCell () { checkIndex (0); return list; } /*********************************************************************** ***********************************************************************/ private Ref lastCell () { checkIndex (0); return list.tail; } /*********************************************************************** ***********************************************************************/ private Ref cellAt (size_t index) { checkIndex (index); return list.nth (index); } /*********************************************************************** ***********************************************************************/ private void checkIndex (size_t index) { if (index >= count) throw new Exception ("out of range"); } /*********************************************************************** Splice elements of e between hd and tl. If hd is null return new hd Returns the count of new elements added ***********************************************************************/ private void splice_ (IContainer!(V) e, Ref hd, Ref tl) { Ref newlist = null; Ref current = null; foreach (v; e) { increment; auto p = heap.allocate.set (v, null); if (newlist is null) newlist = p; else current.next = p; current = p; } if (current) { current.next = tl; if (hd is null) list = newlist; else hd.next = newlist; } } /*********************************************************************** Time complexity: O(n) ***********************************************************************/ private LinkedList clear (bool all) { mutate; // collect each node if we can't collect all at once if (heap.collect(all) is false && count) { auto p = list; while (p) { auto n = p.next; decrement (p); p = n; } } list = null; count = 0; return this; } /*********************************************************************** new element was added ***********************************************************************/ private void increment () { ++mutation; ++count; } /*********************************************************************** element was removed ***********************************************************************/ private void decrement (Ref p) { Reap (p.value); heap.collect (p); ++mutation; --count; } /*********************************************************************** set was changed ***********************************************************************/ private void mutate () { ++mutation; } /*********************************************************************** List iterator ***********************************************************************/ private struct Iterator { Ref node; Ref* hook, prior; LinkedList owner; size_t mutation; /*************************************************************** Did the container change underneath us? ***************************************************************/ bool valid () { return owner.mutation is mutation; } /*************************************************************** Accesses the next value, and returns false when there are no further values to traverse ***************************************************************/ bool next (ref V v) { auto n = next; return (n) ? v = *n, true : false; } /*************************************************************** Return a pointer to the next value, or null when there are no further values to traverse ***************************************************************/ V* next () { V* r; if (node) { prior = hook; r = &node.value; node = *(hook = &node.next); } return r; } /*************************************************************** Insert a new value before the node about to be iterated (or after the node that was just iterated). ***************************************************************/ void insert(V value) { // insert a node previous to the node that we are // about to iterate. *hook = owner.heap.allocate.set(value, *hook); node = *hook; next(); // update the count of the owner, and ignore this // change in the mutation. owner.increment; mutation++; } /*************************************************************** Insert a new value before the value that was just iterated. Returns true if the prior node existed and the insertion worked. False otherwise. ***************************************************************/ bool insertPrior(V value) { if(prior) { // insert a node previous to the node that we just // iterated. *prior = owner.heap.allocate.set(value, *prior); prior = &(*prior).next; // update the count of the owner, and ignore this // change in the mutation. owner.increment; mutation++; return true; } return false; } /*************************************************************** Foreach support ***************************************************************/ int opApply (scope int delegate(ref V value) dg) { int result; auto n = node; while (n) { prior = hook; hook = &n.next; if ((result = dg(n.value)) != 0) break; n = *hook; } node = n; return result; } /*************************************************************** Remove value at the current iterator location ***************************************************************/ bool remove () { if (prior) { auto p = *prior; *prior = p.next; owner.decrement (p); hook = prior; prior = null; // ignore this change ++mutation; return true; } return false; } } } /******************************************************************************* *******************************************************************************/ debug (LinkedList) { import tango.io.Stdout; import tango.core.Thread; import tango.time.StopWatch; void main() { // usage examples ... auto set = new LinkedList!(char[]); set.add ("foo"); set.add ("bar"); set.add ("wumpus"); // implicit generic iteration foreach (value; set) Stdout (value).newline; // explicit generic iteration foreach (value; set.iterator) Stdout.formatln ("{}", value); // generic iteration with optional remove and insert auto s = set.iterator; foreach (value; s) { if (value == "foo") s.remove; if (value == "bar") s.insertPrior("bloomper"); if (value == "wumpus") s.insert("rumple"); } set.check(); // incremental iteration, with optional remove char[] v; auto iterator = set.iterator; while (iterator.next(v)) {} //iterator.remove; // incremental iteration, with optional failfast auto it = set.iterator; while (it.valid && it.next(v)) {} // remove specific element set.remove ("wumpus"); // remove first element ... while (set.take(v)) Stdout.formatln ("taking {}, {} left", v, set.size); // setup for benchmark, with a set of integers. We // use a chunk allocator, and presize the bucket[] auto test = new LinkedList!(int, Container.reap, Container.Chunk); test.cache (2000, 1_000_000); const count = 1_000_000; StopWatch w; // benchmark adding w.start; for (int i=count; i--;) test.prepend(i); Stdout.formatln ("{} adds: {}/s", test.size, test.size/w.stop); // benchmark adding without allocation overhead test.clear; w.start; for (int i=count; i--;) test.prepend(i); Stdout.formatln ("{} adds (after clear): {}/s", test.size, test.size/w.stop); // benchmark duplication w.start; auto dup = test.dup; Stdout.formatln ("{} element dup: {}/s", dup.size, dup.size/w.stop); // benchmark iteration w.start; auto xx = test.iterator; int ii; while (xx.next()) {} Stdout.formatln ("{} element iteration: {}/s", test.size, test.size/w.stop); // benchmark iteration w.start; foreach (v; test) {} Stdout.formatln ("{} foreach iteration: {}/s", test.size, test.size/w.stop); // benchmark iteration w.start; foreach (ref iii; test) {} Stdout.formatln ("{} pointer iteration: {}/s", test.size, test.size/w.stop); test.check; } } |