Add doubly linked list implementation
authorMark Lee <marklee@src.gnome.org>
Thu, 23 Jul 2009 08:15:27 +0000 (10:15 +0200)
committerDidier 'Ptitjes <ptitjes@free.fr>
Thu, 23 Jul 2009 09:02:44 +0000 (11:02 +0200)
Fixes bug 584032.

.gitignore
gee/Makefile.am
gee/linkedlist.vala [new file with mode: 0644]
tests/Makefile.am
tests/testlinkedlist.vala [new file with mode: 0644]

index e0509c1..bd301c2 100644 (file)
@@ -28,5 +28,6 @@ gee/gee-1.0.vapi
 tests/testarraylist
 tests/testhashmap
 tests/testhashset
-
-
+tests/testlinkedlist
+tests/testtreemap
+tests/testtreeset
index dd0b35c..37a1567 100644 (file)
@@ -20,6 +20,7 @@ libgee_la_VALASOURCES = \
        hashset.vala \
        iterable.vala \
        iterator.vala \
+       linkedlist.vala \
        list.vala \
        map.vala \
        readonlycollection.vala \
diff --git a/gee/linkedlist.vala b/gee/linkedlist.vala
new file mode 100644 (file)
index 0000000..0fab4f6
--- /dev/null
@@ -0,0 +1,268 @@
+/* linkedlist.vala
+ *
+ * Copyright (C) 2004-2005  Novell, Inc
+ * Copyright (C) 2005  David Waite
+ * Copyright (C) 2007-2008  Jürg Billeter
+ * Copyright (C) 2009  Mark Lee
+ * Copyright (C) 2009  Julien Fontanet
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
+ *
+ * Author:
+ *     Mark Lee <marklee@src.gnome.org>
+ */
+
+/**
+ * A Gee.List implementation, using a doubly-linked list.
+ */
+public class Gee.LinkedList<G> : Object, Iterable<G>, Collection<G>, List<G> {
+       private int _size = 0;
+       private int _stamp = 0;
+       private Node? _head = null;
+       private Node? _tail = null;
+
+       public EqualFunc equal_func { construct; get; }
+
+       public LinkedList (EqualFunc equal_func = direct_equal) {
+               this.equal_func = equal_func;
+       }
+
+       // Iterable<G>
+       public Type get_element_type () {
+               return typeof (G);
+       }
+
+       public Gee.Iterator<G> iterator () {
+               return new Iterator<G> (this);
+       }
+
+       // Collection<G>
+       public int size {
+               get { return this._size; }
+       }
+
+       public bool contains (G item) {
+               return this.index_of (item) != -1;
+       }
+
+       public bool add (G item) {
+               Node<G> n = new Node<G> (item);
+               if (this._head == null && this._tail == null) {
+                       this._head = this._tail = n;
+               } else {
+                       this._tail.next = n;
+                       n.prev = this._tail;
+                       this._tail = n;
+               }
+
+               // Adding items to the list during iterations is allowed.
+               //++this._stamp;
+
+               this._size++;
+               return true;
+       }
+
+       public bool remove (G item) { // Should remove only the first occurence (a test should be added)
+               for (Node<G> n = this._head; n != null; n = n.next) {
+                       if (this.equal_func (item, n.data)) {
+                               this._remove_node (n);
+                               return true;
+                       }
+               }
+               return false;
+       }
+
+       public void clear () {
+               ++this._stamp;
+               this._head = this._tail = null;
+               this._size = 0;
+       }
+
+       // List<G>
+       public new G? get (int index) {
+               assert (index >= 0);
+               assert (index < this._size);
+
+               unowned Node<G>? n = this._get_node_at (index);
+               if (n == null) {
+                       return null;
+               } else {
+                       return n.data;
+               }
+       }
+
+       public new void set (int index, G item) {
+               assert (index >= 0);
+               assert (index < this._size);
+
+               unowned Node<G>? n = this._get_node_at (index);
+               return_if_fail (n != null);
+               n.data = item;
+       }
+
+       public int index_of (G item) {
+               int result = -1;
+               int idx = 0;
+               foreach (G node_item in this) {
+                       if (this.equal_func (item, node_item)) {
+                               result = idx;
+                               break;
+                       } else {
+                               idx++;
+                       }
+               }
+               return result;
+       }
+
+       public void insert (int index, G item) {
+               assert (index >= 0);
+               assert (index <= this._size);
+
+               if (index == this._size) {
+                       this.add (item);
+               } else {
+                       Node<G> n = new Node<G> (item);
+                       if (index == 0) {
+                               n.next = this._head;
+                               this._head.prev = n;
+                               this._head = (owned)n;
+                       } else {
+                               Node prev = this._head;
+                               for (int i = 0; i < index - 1; i++) {
+                                       prev = prev.next;
+                               }
+                               n.prev = prev;
+                               n.next = prev.next;
+                               n.next.prev = n;
+                               prev.next = n;
+                       }
+
+                       // Adding items to the list during iterations is allowed.
+                       //++this._stamp;
+
+                       this._size++;
+               }
+       }
+
+       public void remove_at (int index) {
+               assert (index >= 0);
+               assert (index < this._size);
+
+               unowned Node<G>? n = this._get_node_at (index);
+               return_if_fail (n != null);
+               this._remove_node (n);
+       }
+
+       public List<G>? slice (int start, int stop) {
+               return_val_if_fail (start <= stop, null);
+               return_val_if_fail (start >= 0, null);
+               return_val_if_fail (stop <= this._size, null);
+
+               List<G> slice = new LinkedList<G> (this.equal_func);
+               Node<G> n = this._get_node_at (start);
+               for (int i = start; i < stop; i++) {
+                       slice.add (n.data);
+                       n = n.next;
+               }
+
+               return slice;
+       }
+
+       private class Node<G> { // Maybe a compact class should be used?
+               public G data;
+               public Node<G>? prev = null;
+               public Node<G>? next = null;
+               public Node (G data) {
+                       this.data = data;
+               }
+       }
+
+       private class Iterator<G> : Object, Gee.Iterator<G> {
+               private bool started = false;
+               private unowned Node<G>? position;
+               private int _stamp;
+               private LinkedList<G> _list;
+
+               public Iterator (LinkedList<G> list) {
+                       this._list = list;
+                       this.position = list._head;
+                       this._stamp = list._stamp;
+               }
+
+               public bool next () {
+                       assert (this._stamp == this._list._stamp);
+
+                       if (!this.started) {
+                               this.started = true;
+                               return this.position != null;
+                       } else if (this.position.next == null) {
+                               return false;
+                       } else {
+                               this.position = this.position.next;
+                               return true;
+                       }
+               }
+
+               public new G? get () {
+                       assert (this._stamp == this._list._stamp);
+
+                       if (this.position == null) {
+                               return null;
+                       } else {
+                               return this.position.data;
+                       }
+               }
+       }
+
+       private unowned Node<G>? _get_node_at (int index) {
+               unowned Node<G>? n = null;;
+               if (index == 0) {
+                       n = this._head;
+               } else if (index == this._size - 1) {
+                       n = this._tail;
+               } else if (index <= this._size / 2) {
+                       n = this._head;
+                       for (int i = 0; index != i; i++) {
+                               n = n.next;
+                       }
+               } else {
+                       n = this._tail;
+                       for (int i = this._size - 1; index != i; i--) {
+                               n = n.prev;
+                       }
+               }
+               return n;
+       }
+
+       private void _remove_node (owned Node<G> n) {
+               if (n == this._head) {
+                       this._head = n.next;
+               }
+               if (n == this._tail) {
+                       this._tail = n.prev;
+               }
+               if (n.prev != null) {
+                       n.prev.next = n.next;
+               }
+               if (n.next != null) {
+                       n.next.prev = n.prev;
+               }
+               n.prev = null;
+               n.next = null;
+               ++this._stamp;
+               this._size--;
+       }
+}
+
index 68cfec5..3f5879f 100644 (file)
@@ -38,6 +38,15 @@ $(testhashset_SOURCES): $(testhashset_VALASOURCES)
 testhashset_LDADD = $(progs_ldadd)
 EXTRA_DIST += $(testhashset_VALASOURCES)
 
+TEST_PROGS += testlinkedlist
+testlinkedlist_VALASOURCES = testlinkedlist.vala
+testlinkedlist_SOURCES = testlinkedlist.c
+$(testlinkedlist_SOURCES): $(testlinkedlist_VALASOURCES)
+       $(VALAC) -C --basedir $(top_srcdir) --vapidir $(top_srcdir)/gee --pkg gee-1.0 $^
+       touch $@
+testlinkedlist_LDADD = $(progs_ldadd)
+EXTRA_DIST += $(testlinkedlist_VALASOURCES)
+
 TEST_PROGS += testtreeset
 testtreeset_VALASOURCES = testtreeset.vala
 testtreeset_SOURCES = testtreeset.c testtreeset.h
diff --git a/tests/testlinkedlist.vala b/tests/testlinkedlist.vala
new file mode 100644 (file)
index 0000000..3779375
--- /dev/null
@@ -0,0 +1,579 @@
+/* testlinkedlist.vala
+ *
+ * Copyright (C) 2008  Jürg Billeter
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
+ *
+ * Authors:
+ *     Jürg Billeter <j@bitron.ch>
+ *     Mark Lee <marklee@src.gnome.org> (port to LinkedList)
+ */
+
+using GLib;
+using Gee;
+
+void test_linkedlist_get () {
+       var linkedlistOfString = new LinkedList<string> (str_equal);
+
+       // Check get for empty list
+       if (Test.trap_fork (0, TestTrapFlags.SILENCE_STDOUT | TestTrapFlags.SILENCE_STDERR)) {
+               linkedlistOfString.get (0);
+               return;
+       }
+       Test.trap_assert_failed ();
+
+       // Check get for valid index in list with one element
+       linkedlistOfString.add ("1");
+       assert (linkedlistOfString.get (0) == "1");
+
+       // Check get for indexes out of range
+       if (Test.trap_fork (0, TestTrapFlags.SILENCE_STDOUT | TestTrapFlags.SILENCE_STDERR)) {
+               linkedlistOfString.get (1);
+               return;
+       }
+       Test.trap_assert_failed ();
+
+       // Check get for invalid index number
+       if (Test.trap_fork (0, TestTrapFlags.SILENCE_STDOUT | TestTrapFlags.SILENCE_STDERR)) {
+               linkedlistOfString.get (-1);
+               return;
+       }
+       Test.trap_assert_failed ();
+
+       // Check get for valid indexes in list with multiple element
+       linkedlistOfString.add ("2");
+       linkedlistOfString.add ("3");
+       assert (linkedlistOfString.get (0) == "1");
+       assert (linkedlistOfString.get (1) == "2");
+       assert (linkedlistOfString.get (2) == "3");
+
+       // Check get if list is cleared and empty again
+       linkedlistOfString.clear ();
+
+       if (Test.trap_fork (0, TestTrapFlags.SILENCE_STDOUT | TestTrapFlags.SILENCE_STDERR)) {
+               linkedlistOfString.get (0);
+               return;
+       }
+       Test.trap_assert_failed ();
+}
+
+void test_linkedlist_set () {
+       var linkedlistOfString = new LinkedList<string> (str_equal);
+
+       // Check set when list is empty.
+       assert (linkedlistOfString.size == 0);
+       if (Test.trap_fork (0, TestTrapFlags.SILENCE_STDOUT | TestTrapFlags.SILENCE_STDERR)) {
+               linkedlistOfString.set (0, "0");
+               return;
+       }
+       Test.trap_assert_failed ();
+       assert (linkedlistOfString.size == 0);
+
+       // Check set when one item is in list
+       linkedlistOfString.add ("1"); // Add item "1"
+       assert (linkedlistOfString.size == 1);
+       assert (linkedlistOfString.get (0) == "1");
+
+       linkedlistOfString.set (0, "2"); // Set the item to value 2
+       assert (linkedlistOfString.size == 1);
+       assert (linkedlistOfString.get (0) == "2");
+
+       // Check set when index out of range
+       assert (linkedlistOfString.size == 1);
+       if (Test.trap_fork (0, TestTrapFlags.SILENCE_STDOUT | TestTrapFlags.SILENCE_STDERR)) {
+               linkedlistOfString.set (1, "0");
+               return;
+       }
+       Test.trap_assert_failed ();
+       assert (linkedlistOfString.size == 1);
+}
+
+void test_linkedlist_insert () {
+       var linkedlistOfString = new LinkedList<string> (str_equal);
+
+       // Check inserting in empty list
+       // Inserting at index 1
+       if (Test.trap_fork (0, TestTrapFlags.SILENCE_STDOUT | TestTrapFlags.SILENCE_STDERR)) {
+               linkedlistOfString.insert (1, "0");
+               return;
+       }
+       Test.trap_assert_failed ();
+
+       // Inserting at index 0
+       assert (linkedlistOfString.size == 0);
+       linkedlistOfString.insert (0, "10");
+       assert (linkedlistOfString.size == 1);
+       assert (linkedlistOfString.get (0) == "10");
+
+       // Check insert to the beginning
+       linkedlistOfString.insert (0, "5");
+       assert (linkedlistOfString.get (0) == "5");
+       assert (linkedlistOfString.get (1) == "10");
+
+       // Check insert in between
+       linkedlistOfString.insert (1, "7");
+       assert (linkedlistOfString.get (0) == "5");
+       assert (linkedlistOfString.get (1) == "7");
+       assert (linkedlistOfString.get (2) == "10");
+
+       // Check insert into index out of current range
+       if (Test.trap_fork (0, TestTrapFlags.SILENCE_STDOUT | TestTrapFlags.SILENCE_STDERR)) {
+               linkedlistOfString.insert (4, "20");
+               return;
+       }
+       Test.trap_assert_failed ();
+
+       // Check insert to the end
+       linkedlistOfString.insert (3, "20");
+       assert (linkedlistOfString.get (0) == "5");
+       assert (linkedlistOfString.get (1) == "7");
+       assert (linkedlistOfString.get (2) == "10");
+       assert (linkedlistOfString.get (3) == "20");
+
+       // Check insert into invalid index
+       if (Test.trap_fork (0, TestTrapFlags.SILENCE_STDOUT | TestTrapFlags.SILENCE_STDERR)) {
+               linkedlistOfString.insert (-1, "0");
+               return;
+       }
+       Test.trap_assert_failed ();
+
+}
+
+void test_linkedlist_remove_at () {
+       var linkedlistOfString = new LinkedList<string> (str_equal);
+
+       // Check removing in empty list
+       if (Test.trap_fork (0, TestTrapFlags.SILENCE_STDOUT | TestTrapFlags.SILENCE_STDERR)) {
+               linkedlistOfString.remove_at (0);
+               return;
+       }
+       Test.trap_assert_failed ();
+
+       if (Test.trap_fork (0, TestTrapFlags.SILENCE_STDOUT | TestTrapFlags.SILENCE_STDERR)) {
+               linkedlistOfString.remove_at (1);
+               return;
+       }
+       Test.trap_assert_failed ();
+
+       // add 5 items
+       linkedlistOfString.add ("1");
+       linkedlistOfString.add ("2");
+       linkedlistOfString.add ("3");
+       linkedlistOfString.add ("4");
+       linkedlistOfString.add ("5");
+       assert (linkedlistOfString.size == 5);
+
+       // Check remove_at first
+       linkedlistOfString.remove_at (0);
+       assert (linkedlistOfString.size == 4);
+       assert (linkedlistOfString.get (0) == "2");
+       assert (linkedlistOfString.get (1) == "3");
+       assert (linkedlistOfString.get (2) == "4");
+       assert (linkedlistOfString.get (3) == "5");
+
+       // Check remove_at last
+       linkedlistOfString.remove_at (3);
+       assert (linkedlistOfString.size == 3);
+       assert (linkedlistOfString.get (0) == "2");
+       assert (linkedlistOfString.get (1) == "3");
+       assert (linkedlistOfString.get (2) == "4");
+
+       // Check remove_at in between
+       linkedlistOfString.remove_at (1);
+       assert (linkedlistOfString.size == 2);
+       assert (linkedlistOfString.get (0) == "2");
+       assert (linkedlistOfString.get (1) == "4");
+
+       // Check remove_at when index out of range
+       if (Test.trap_fork (0, TestTrapFlags.SILENCE_STDOUT | TestTrapFlags.SILENCE_STDERR)) {
+               linkedlistOfString.remove_at (2);
+               return;
+       }
+       Test.trap_assert_failed ();
+
+       // Check remove_at when invalid index
+       if (Test.trap_fork (0, TestTrapFlags.SILENCE_STDOUT | TestTrapFlags.SILENCE_STDERR)) {
+               linkedlistOfString.remove_at (-1);
+               return;
+       }
+       Test.trap_assert_failed ();
+}
+
+void test_linkedlist_index_of () {
+       var linkedlistOfString = new LinkedList<string> (str_equal);
+       // Check empty list
+       assert (linkedlistOfString.index_of ("one") == -1);
+
+       // Check one item
+       linkedlistOfString.add ("one");
+       assert (linkedlistOfString.index_of ("one") == 0);
+       assert (linkedlistOfString.index_of ("two") == -1);
+
+       // Check more items
+       linkedlistOfString.add ("two");
+       linkedlistOfString.add ("three");
+       linkedlistOfString.add ("four");
+       assert (linkedlistOfString.index_of ("one") == 0);
+       assert (linkedlistOfString.index_of ("two") == 1);
+       assert (linkedlistOfString.index_of ("three") == 2);
+       assert (linkedlistOfString.index_of ("four") == 3);
+       assert (linkedlistOfString.index_of ("five") == -1);
+
+       // Check list of ints
+       var linkedlistOfInt = new LinkedList<int> ();
+
+       // Check more items
+       linkedlistOfInt.add (1);
+       linkedlistOfInt.add (2);
+       linkedlistOfInt.add (3);
+       assert (linkedlistOfInt.index_of (1) == 0);
+       assert (linkedlistOfInt.index_of (2) == 1);
+       assert (linkedlistOfInt.index_of (3) == 2);
+       assert (linkedlistOfInt.index_of (4) == -1);
+
+       // Check list of objects
+       var linkedlistOfObjects = new LinkedList<Object> ();
+
+       var object1 = new Object ();
+       var object2 = new Object ();
+       var object3 = new Object ();
+       var object4 = new Object ();
+
+       linkedlistOfObjects.add (object1);
+       linkedlistOfObjects.add (object2);
+       linkedlistOfObjects.add (object3);
+       linkedlistOfObjects.add (object4);
+
+       assert (linkedlistOfObjects.index_of (object1) == 0);
+       assert (linkedlistOfObjects.index_of (object2) == 1);
+       assert (linkedlistOfObjects.index_of (object3) == 2);
+       assert (linkedlistOfObjects.index_of (object4) == 3);
+
+}
+
+void test_linkedlist_slice () {
+       var linkedlistOfString = new LinkedList<string> (str_equal);
+       Gee.List<string> slicedLinkedListOfString;
+       // Check empty list
+       assert (linkedlistOfString.slice (0, 0).size == 0);
+
+       // Check one item
+       linkedlistOfString.add ("one");
+       slicedLinkedListOfString = linkedlistOfString.slice (0, 1);
+       assert (slicedLinkedListOfString.size == 1);
+       assert (slicedLinkedListOfString.get (0) == "one");
+
+       // Check more items
+       linkedlistOfString.add ("two");
+       linkedlistOfString.add ("three");
+       linkedlistOfString.add ("four");
+       slicedLinkedListOfString = linkedlistOfString.slice (1, 2);
+       assert (slicedLinkedListOfString.size == 1);
+       assert (slicedLinkedListOfString.get (0) == "two");
+       slicedLinkedListOfString = linkedlistOfString.slice (2, 4);
+       assert (slicedLinkedListOfString.size == 2);
+       assert (slicedLinkedListOfString.get (0) == "three");
+       assert (slicedLinkedListOfString.get (1) == "four");
+
+       // Check slice when start/stop out of range
+       if (Test.trap_fork (0, TestTrapFlags.SILENCE_STDOUT | TestTrapFlags.SILENCE_STDERR)) {
+               linkedlistOfString.slice (-1, 0);
+               return;
+       }
+       Test.trap_assert_failed ();
+       if (Test.trap_fork (0, TestTrapFlags.SILENCE_STDOUT | TestTrapFlags.SILENCE_STDERR)) {
+               linkedlistOfString.slice (0, -1);
+               return;
+       }
+       Test.trap_assert_failed ();
+
+       // Check slice when start > stop
+       if (Test.trap_fork (0, TestTrapFlags.SILENCE_STDOUT | TestTrapFlags.SILENCE_STDERR)) {
+               linkedlistOfString.slice (1, 0);
+               return;
+       }
+       Test.trap_assert_failed ();
+
+       // Check slice when stop > size
+       if (Test.trap_fork (0, TestTrapFlags.SILENCE_STDOUT | TestTrapFlags.SILENCE_STDERR)) {
+               linkedlistOfString.slice (1, 5);
+               return;
+       }
+       Test.trap_assert_failed ();
+}
+
+void test_linkedlist_add () {
+       var linkedlistOfString = new LinkedList<string> (str_equal);
+
+       linkedlistOfString.add ("42");
+       assert (linkedlistOfString.contains ("42"));
+       assert (linkedlistOfString.size == 1);
+
+       // check for correct order of elements
+       linkedlistOfString.add ("43");
+       linkedlistOfString.add ("44");
+       linkedlistOfString.add ("45");
+       assert (linkedlistOfString.get (0) == "42");
+       assert (linkedlistOfString.get (1) == "43");
+       assert (linkedlistOfString.get (2) == "44");
+       assert (linkedlistOfString.get (3) == "45");
+       assert (linkedlistOfString.size == 4);
+
+       // check adding of ints
+       var linkedlistOfInt = new LinkedList<int> ();
+
+       linkedlistOfInt.add (42);
+       assert (linkedlistOfInt.contains (42));
+       assert (linkedlistOfInt.size == 1);
+
+       // check adding of objects
+       var linkedlistOfGLibObject = new LinkedList<Object> ();
+
+       var fooObject = new Object ();
+       linkedlistOfGLibObject.add (fooObject);
+       assert (linkedlistOfGLibObject.contains (fooObject));
+       assert (linkedlistOfGLibObject.size == 1);
+
+}
+
+void test_linkedlist_clear () {
+       var linkedlistOfString = new LinkedList<string> (str_equal);
+       assert (linkedlistOfString.size == 0);
+
+       // Check clear on empty list
+       linkedlistOfString.clear ();
+       assert (linkedlistOfString.size == 0);
+
+       // Check clear one item
+       linkedlistOfString.add ("1");
+       assert (linkedlistOfString.size == 1);
+       linkedlistOfString.clear ();
+       assert (linkedlistOfString.size == 0);
+
+       // Check clear multiple items
+       linkedlistOfString.add ("1");
+       linkedlistOfString.add ("2");
+       linkedlistOfString.add ("3");
+       assert (linkedlistOfString.size == 3);
+       linkedlistOfString.clear ();
+       assert (linkedlistOfString.size == 0);
+}
+
+void test_linkedlist_contains () {
+       var linkedlistOfString = new LinkedList<string> (str_equal);
+
+       // Check on empty list
+       assert (!linkedlistOfString.contains ("1"));
+
+       // Check items
+       linkedlistOfString.add ("10");
+       assert (linkedlistOfString.contains ("10"));
+       assert (!linkedlistOfString.contains ("20"));
+       assert (!linkedlistOfString.contains ("30"));
+
+       linkedlistOfString.add ("20");
+       assert (linkedlistOfString.contains ("10"));
+       assert (linkedlistOfString.contains ("20"));
+       assert (!linkedlistOfString.contains ("30"));
+
+       linkedlistOfString.add ("30");
+       assert (linkedlistOfString.contains ("10"));
+       assert (linkedlistOfString.contains ("20"));
+       assert (linkedlistOfString.contains ("30"));
+
+       // Clear and recheck
+       linkedlistOfString.clear ();
+       assert (!linkedlistOfString.contains ("10"));
+       assert (!linkedlistOfString.contains ("20"));
+       assert (!linkedlistOfString.contains ("30"));
+
+       var linkedlistOfInt = new LinkedList<int> ();
+
+       // Check items
+       linkedlistOfInt.add (10);
+       assert (linkedlistOfInt.contains (10));
+       assert (!linkedlistOfInt.contains (20));
+       assert (!linkedlistOfInt.contains (30));
+
+       linkedlistOfInt.add (20);
+       assert (linkedlistOfInt.contains (10));
+       assert (linkedlistOfInt.contains (20));
+       assert (!linkedlistOfInt.contains (30));
+
+       linkedlistOfInt.add (30);
+       assert (linkedlistOfInt.contains (10));
+       assert (linkedlistOfInt.contains (20));
+       assert (linkedlistOfInt.contains (30));
+
+       // Clear and recheck
+       linkedlistOfInt.clear ();
+       assert (!linkedlistOfInt.contains (10));
+       assert (!linkedlistOfInt.contains (20));
+       assert (!linkedlistOfInt.contains (30));
+}
+
+void test_linkedlist_remove () {
+       var linkedlistOfString = new LinkedList<string> (str_equal);
+
+       // Check remove if list is empty
+       linkedlistOfString.remove ("42");
+
+       // Add 5 same elements
+       linkedlistOfString.add ("42");
+       linkedlistOfString.add ("42");
+       linkedlistOfString.add ("42");
+       linkedlistOfString.add ("42");
+       linkedlistOfString.add ("42");
+
+       // Check remove one element
+       linkedlistOfString.remove ("42");
+       assert (linkedlistOfString.size == 4);
+       assert (linkedlistOfString.contains ("42"));
+
+       // Check remove another one element
+       linkedlistOfString.remove ("42");
+       assert (linkedlistOfString.size == 3);
+       assert (linkedlistOfString.contains ("42"));
+
+       // Clear the list to start from scratch
+       linkedlistOfString.clear ();
+
+       // Add 5 different elements
+       linkedlistOfString.add ("42");
+       linkedlistOfString.add ("43");
+       linkedlistOfString.add ("44");
+       linkedlistOfString.add ("45");
+       linkedlistOfString.add ("46");
+       assert (linkedlistOfString.size == 5);
+
+       // Check remove first
+       linkedlistOfString.remove ("42");
+       assert (linkedlistOfString.size == 4);
+       assert (linkedlistOfString.get (0) == "43");
+       assert (linkedlistOfString.get (1) == "44");
+       assert (linkedlistOfString.get (2) == "45");
+       assert (linkedlistOfString.get (3) == "46");
+
+       // Check remove last
+       linkedlistOfString.remove ("46");
+       assert (linkedlistOfString.size == 3);
+       assert (linkedlistOfString.get (0) == "43");
+       assert (linkedlistOfString.get (1) == "44");
+       assert (linkedlistOfString.get (2) == "45");
+
+       // Check remove in between
+       linkedlistOfString.remove ("44");
+       assert (linkedlistOfString.size == 2);
+       assert (linkedlistOfString.get (0) == "43");
+       assert (linkedlistOfString.get (1) == "45");
+
+       // Check removing of int element
+       var linkedlistOfInt = new LinkedList<int> ();
+
+       // Add 5 different elements
+       linkedlistOfInt.add (42);
+       linkedlistOfInt.add (43);
+       linkedlistOfInt.add (44);
+       linkedlistOfInt.add (45);
+       linkedlistOfInt.add (46);
+       assert (linkedlistOfInt.size == 5);
+
+       // Remove first
+       linkedlistOfInt.remove (42);
+       assert (linkedlistOfInt.size == 4);
+       assert (linkedlistOfInt.get (0) == 43);
+       assert (linkedlistOfInt.get (1) == 44);
+       assert (linkedlistOfInt.get (2) == 45);
+       assert (linkedlistOfInt.get (3) == 46);
+
+       // Remove last
+       linkedlistOfInt.remove (46);
+       assert (linkedlistOfInt.size == 3);
+       assert (linkedlistOfInt.get (0) == 43);
+       assert (linkedlistOfInt.get (1) == 44);
+       assert (linkedlistOfInt.get (2) == 45);
+
+       // Remove in between
+       linkedlistOfInt.remove (44);
+       assert (linkedlistOfInt.size == 2);
+       assert (linkedlistOfInt.get (0) == 43);
+       assert (linkedlistOfInt.get (1) == 45);
+}
+
+void test_linkedlist_size () {
+       var linkedlist = new LinkedList<string> (str_equal);
+
+       // Check empty list
+       assert (linkedlist.size == 0);
+
+       // Check when one item
+       linkedlist.add ("1");
+       assert (linkedlist.size == 1);
+
+       // Check when more items
+       linkedlist.add ("2");
+       assert (linkedlist.size == 2);
+
+       // Check when items cleared
+       linkedlist.clear ();
+       assert (linkedlist.size == 0);
+}
+
+void test_linkedlist_iterator () {
+       var linkedlistOfString = new LinkedList<string> (str_equal);
+
+       // Check iterate empty list
+       var iterator = linkedlistOfString.iterator ();
+       assert (!iterator.next ());
+
+       // Check iterate list
+       linkedlistOfString.add ("42");
+       linkedlistOfString.add ("43");
+       linkedlistOfString.add ("44");
+
+       iterator = linkedlistOfString.iterator ();
+       assert (iterator.next ());
+       assert (iterator.get () == "42");
+       assert (iterator.next ());
+       assert (iterator.get () == "43");
+       assert (iterator.next ());
+       assert (iterator.get () == "44");
+       assert (!iterator.next ());
+}
+
+void main (string[] args) {
+       Test.init (ref args);
+
+       // Methods of List interface
+       Test.add_func ("/LinkedList/List/get", test_linkedlist_get);
+       Test.add_func ("/LinkedList/List/set", test_linkedlist_set);
+       Test.add_func ("/LinkedList/List/insert", test_linkedlist_insert);
+       Test.add_func ("/LinkedList/List/remove_at", test_linkedlist_remove_at);
+       Test.add_func ("/LinkedList/List/index_of", test_linkedlist_index_of);
+       Test.add_func ("/LinkedList/List/slice", test_linkedlist_slice);
+
+       // Methods of Collection interface
+       Test.add_func ("/LinkedList/Collection/add", test_linkedlist_add);
+       Test.add_func ("/LinkedList/Collection/clear", test_linkedlist_clear);
+       Test.add_func ("/LinkedList/Collection/contains", test_linkedlist_contains);
+       Test.add_func ("/LinkedList/Collection/remove", test_linkedlist_remove);
+       Test.add_func ("/LinkedList/Collection/size", test_linkedlist_size);
+
+       // Methods of Iterable interface
+       Test.add_func ("/LinkedList/Iterable/iterator", test_linkedlist_iterator);
+
+       Test.run ();
+}
+