From b00bc5891c3ee8da687395faf683b8dd0703ede5 Mon Sep 17 00:00:00 2001 From: Antao Almada Date: Wed, 9 Jun 2021 22:35:19 +0100 Subject: [PATCH] 3.0 --- Directory.Build.props | 11 +- .../ConstructorBenchmark.cs | 16 +- .../EnumerationBenchmark.cs | 104 ++++++------ ...etFabric.DoublyLinkedList.Benchmark.csproj | 4 +- .../AddAfterTests.cs | 12 +- .../AddBeforeTests.cs | 12 +- .../AddFirstTests.cs | 88 +++++----- .../AddLastTests.cs | 88 +++++----- .../AppendTests.cs | 26 +-- .../ClearTests.cs | 12 +- .../CloneTests.cs | 12 +- .../FindLastTests.cs | 4 +- NetFabric.DoublyLinkedList.Tests/FindTests.cs | 4 +- .../ForwardEnumerationTests.cs | 10 +- .../NetFabric.DoublyLinkedList.Tests.csproj | 2 +- .../RemoveTests.cs | 52 +++--- .../ReverseEnumerationTests.cs | 10 +- .../ReverseTests.cs | 16 +- .../DoublyLinkedList'1.ForwardEnumeration.cs | 107 +++++------- .../DoublyLinkedList'1.Node.cs | 6 +- .../DoublyLinkedList'1.ReverseEnumeration.cs | 106 +++++------- .../DoublyLinkedList'1.cs | 154 ++++++++---------- .../DoublyLinkedList.cs | 11 +- NetFabric.DoublyLinkedList/EnumeratorState.cs | 11 -- .../NetFabric.DoublyLinkedList.csproj | 14 +- NetFabric.DoublyLinkedList/Throw.cs | 39 +++++ README.md | 68 ++++++-- 27 files changed, 501 insertions(+), 498 deletions(-) delete mode 100644 NetFabric.DoublyLinkedList/EnumeratorState.cs create mode 100644 NetFabric.DoublyLinkedList/Throw.cs diff --git a/Directory.Build.props b/Directory.Build.props index e44f23f..e6b0cf5 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,15 +1,8 @@ - 8.0 + 9.0 strict + enable - - - - all - runtime; build; native; contentfiles; analyzers - - - \ No newline at end of file diff --git a/NetFabric.DoublyLinkedList.Benchmark/ConstructorBenchmark.cs b/NetFabric.DoublyLinkedList.Benchmark/ConstructorBenchmark.cs index dbdf5f9..003b261 100644 --- a/NetFabric.DoublyLinkedList.Benchmark/ConstructorBenchmark.cs +++ b/NetFabric.DoublyLinkedList.Benchmark/ConstructorBenchmark.cs @@ -2,14 +2,16 @@ using System.Collections.Generic; using System.Linq; using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Configs; namespace NetFabric.Benchmark { + [MarkdownExporterAttribute.GitHub] [MemoryDiagnoser] public class ConstructorBenchmark { - IEnumerable enumerable; - IReadOnlyList collection; + IEnumerable? enumerable; + IReadOnlyList? collection; [Params(0, 10, 100)] public int Count {get; set;} @@ -23,22 +25,22 @@ public void GlobalSetup() [Benchmark(Baseline = true)] public LinkedList LinkedList_Enumerable() => - new LinkedList(enumerable); + new(enumerable!); [Benchmark] public LinkedList LinkedList_List() => - new LinkedList(collection); + new(collection!); [Benchmark] public DoublyLinkedList DoublyLinkedList_Enumerable() => - new DoublyLinkedList(enumerable); + new(enumerable!); [Benchmark] public DoublyLinkedList DoublyLinkedList_List() => - new DoublyLinkedList(collection, false); + new(collection!, reversed: false); [Benchmark] public DoublyLinkedList DoublyLinkedList_List_Reversed() => - new DoublyLinkedList(collection, true); + new(collection!, reversed: true); } } diff --git a/NetFabric.DoublyLinkedList.Benchmark/EnumerationBenchmark.cs b/NetFabric.DoublyLinkedList.Benchmark/EnumerationBenchmark.cs index 02c6d82..971fa64 100644 --- a/NetFabric.DoublyLinkedList.Benchmark/EnumerationBenchmark.cs +++ b/NetFabric.DoublyLinkedList.Benchmark/EnumerationBenchmark.cs @@ -2,110 +2,108 @@ using System.Collections.Generic; using System.Linq; using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Configs; namespace NetFabric.Benchmark { + [MarkdownExporterAttribute.GitHub] + [GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)] + [CategoriesColumn] [MemoryDiagnoser] public class EnumerationBenchmark { - const int count = 10_000; - LinkedList linkedList; - DoublyLinkedList doublyLinkedList; + LinkedList? linkedList; + DoublyLinkedList? doublyLinkedList; + + [Params(100_000)] + public int Count { get; set; } [GlobalSetup] public void GlobalSetup() { - var data = Enumerable.Range(0, count); + var data = Enumerable.Range(0, Count); linkedList = new LinkedList(data); doublyLinkedList = new DoublyLinkedList(data); } + [BenchmarkCategory("Forward")] [Benchmark(Baseline = true)] public int LinkedList_Forward_While() { - var count = 0; - var current = linkedList.First; - while (current is object) - { - count += current.Value; - current = current.Next; - } - return count; + var sum = 0; + for (var current = linkedList!.First; current is not null; current = current.Next) + sum += current.Value; + return sum; } + [BenchmarkCategory("Forward")] [Benchmark] public int LinkedList_Forward_ForEach() { - var count = 0; - foreach (var value in linkedList) - count += value; - return count; + var sum = 0; + foreach (var value in linkedList!) + sum += value; + return sum; } - [Benchmark] + [BenchmarkCategory("Reverse")] + [Benchmark(Baseline = true)] public int LinkedList_Reverse_While() { - var count = 0; - var current = linkedList.Last; - while (current is object) - { - count += current.Value; - current = current.Previous; - } - return count; + var sum = 0; + for (var current = linkedList!.Last; current is not null; current = current.Previous) + sum += current.Value; + return sum; } + [BenchmarkCategory("Reverse")] [Benchmark] public int LinkedList_Reverse_ForEach() { - var count = 0; - foreach (var value in linkedList.Reverse()) - count += value; - return count; + var sum = 0; + foreach (var value in linkedList!.Reverse()) + sum += value; + return sum; } + [BenchmarkCategory("Forward")] [Benchmark] public int DoublyLinkedList_Forward_While() { - var count = 0; - var current = doublyLinkedList.First; - while (current is object) - { - count += current.Value; - current = current.Next; - } - return count; + var sum = 0; + for (var current = doublyLinkedList!.First; current is not null; current = current.Next) + sum += current.Value; + return sum; } + [BenchmarkCategory("Forward")] [Benchmark] public int DoublyLinkedList_Forward_ForEach() { - var count = 0; - foreach (var value in doublyLinkedList.EnumerateForward()) - count += value; - return count; + var sum = 0; + foreach (var value in doublyLinkedList!.Forward) + sum += value; + return sum; } + [BenchmarkCategory("Reverse")] [Benchmark] public int DoublyLinkedList_Reverse_While() { - var count = 0; - var current = doublyLinkedList.Last; - while (current is object) - { - count += current.Value; - current = current.Previous; - } - return count; + var sum = 0; + for (var current = doublyLinkedList!.Last; current is not null; current = current.Previous) + sum += current.Value; + return sum; } + [BenchmarkCategory("Reverse")] [Benchmark] public int DoublyLinkedList_Reverse_ForEach() { - var count = 0; - foreach (var value in doublyLinkedList.EnumerateReversed()) - count += value; - return count; + var sum = 0; + foreach (var value in doublyLinkedList!.Backward) + sum += value; + return sum; } } } diff --git a/NetFabric.DoublyLinkedList.Benchmark/NetFabric.DoublyLinkedList.Benchmark.csproj b/NetFabric.DoublyLinkedList.Benchmark/NetFabric.DoublyLinkedList.Benchmark.csproj index 1045724..3b1ecf3 100644 --- a/NetFabric.DoublyLinkedList.Benchmark/NetFabric.DoublyLinkedList.Benchmark.csproj +++ b/NetFabric.DoublyLinkedList.Benchmark/NetFabric.DoublyLinkedList.Benchmark.csproj @@ -2,12 +2,12 @@ Exe - netcoreapp3.0 + net5.0 false - + diff --git a/NetFabric.DoublyLinkedList.Tests/AddAfterTests.cs b/NetFabric.DoublyLinkedList.Tests/AddAfterTests.cs index 3c6fa93..459372f 100644 --- a/NetFabric.DoublyLinkedList.Tests/AddAfterTests.cs +++ b/NetFabric.DoublyLinkedList.Tests/AddAfterTests.cs @@ -29,7 +29,7 @@ void InvalidNode() { // Arrange var list = new DoublyLinkedList(); - var anotherList = new DoublyLinkedList(new int[] { 1 }); + var anotherList = new DoublyLinkedList(new[] { 1 }); var node = anotherList.Find(1); // Act @@ -41,10 +41,10 @@ void InvalidNode() } public static TheoryData, int, int, IReadOnlyList> ItemData => - new TheoryData, int, int, IReadOnlyList> + new() { - { new int[] { 1 }, 1, 2, new int[] { 1, 2 } }, - { new int[] { 1, 3 }, 1, 2, new int[] { 1, 2, 3 } }, + { new[] { 1 }, 1, 2, new[] { 1, 2 } }, + { new[] { 1, 3 }, 1, 2, new[] { 1, 2, 3 } }, }; [Theory] @@ -62,10 +62,10 @@ void AddItem(IReadOnlyList collection, int after, int item, IReadOnlyList() .BeEqualTo(expected); - list.EnumerateReversed().Must() + list.Backward.Must() .BeEnumerableOf() .BeEqualTo(expected.Reverse()); } diff --git a/NetFabric.DoublyLinkedList.Tests/AddBeforeTests.cs b/NetFabric.DoublyLinkedList.Tests/AddBeforeTests.cs index 50a709e..3b24d14 100644 --- a/NetFabric.DoublyLinkedList.Tests/AddBeforeTests.cs +++ b/NetFabric.DoublyLinkedList.Tests/AddBeforeTests.cs @@ -29,7 +29,7 @@ void InvalidNode() { // Arrange var list = new DoublyLinkedList(); - var anotherList = new DoublyLinkedList(new int[] { 1 }); + var anotherList = new DoublyLinkedList(new[] { 1 }); var node = anotherList.Find(1); // Act @@ -41,10 +41,10 @@ void InvalidNode() } public static TheoryData, int, int, IReadOnlyList> ItemData => - new TheoryData, int, int, IReadOnlyList> + new() { - { new int[] { 2 }, 2, 1, new int[] { 1, 2 } }, - { new int[] { 1, 3 }, 3, 2, new int[] { 1, 2, 3 } }, + { new[] { 2 }, 2, 1, new[] { 1, 2 } }, + { new[] { 1, 3 }, 3, 2, new[] { 1, 2, 3 } }, }; [Theory] @@ -62,10 +62,10 @@ void AddItem(IReadOnlyList collection, int after, int item, IReadOnlyList() .BeEqualTo(expected); - list.EnumerateReversed().Must() + list.Backward.Must() .BeEnumerableOf() .BeEqualTo(expected.Reverse()); } diff --git a/NetFabric.DoublyLinkedList.Tests/AddFirstTests.cs b/NetFabric.DoublyLinkedList.Tests/AddFirstTests.cs index ff27a51..dc8978a 100644 --- a/NetFabric.DoublyLinkedList.Tests/AddFirstTests.cs +++ b/NetFabric.DoublyLinkedList.Tests/AddFirstTests.cs @@ -57,11 +57,11 @@ void AddNullList() } public static TheoryData, int, IReadOnlyList> ItemData => - new TheoryData, int, IReadOnlyList> + new() { - { new int[] { }, 1, new int[] { 1 } }, - { new int[] { 2 }, 1, new int[] { 1, 2 } }, - { new int[] { 2, 3, 4, 5 }, 1, new int[] { 1, 2, 3, 4, 5 } }, + { Array.Empty(), 1, new[] { 1 } }, + { new[] { 2 }, 1, new[] { 1, 2 } }, + { new[] { 2, 3, 4, 5 }, 1, new[] { 1, 2, 3, 4, 5 } }, }; [Theory] @@ -78,26 +78,26 @@ void AddItem(IReadOnlyList collection, int item, IReadOnlyList expecte // Assert list.Version.Must() .BeNotEqualTo(version); - list.EnumerateForward().Must() + list.Forward.Must() .BeEnumerableOf() .BeEqualTo(expected); - list.EnumerateReversed().Must() + list.Backward.Must() .BeEnumerableOf() .BeEqualTo(expected.Reverse()); } public static TheoryData, IReadOnlyList, bool, IReadOnlyList> CollectionData => - new TheoryData, IReadOnlyList, bool, IReadOnlyList> + new() { - { new int[] { }, new int[] { }, false, new int[] { } }, - { new int[] { }, new int[] { 1 }, true, new int[] { 1 } }, - { new int[] { }, new int[] { 1, 2, 3, 4, 5 }, true, new int[] { 1, 2, 3, 4, 5 } }, - { new int[] { 1 }, new int[] { }, false, new int[] { 1 } }, - { new int[] { 2 }, new int[] { 1 }, true, new int[] { 1, 2 } }, - { new int[] { 5 }, new int[] { 1, 2, 3, 4 }, true, new int[] { 1, 2, 3, 4, 5 } }, - { new int[] { 1, 2, 3, 4, 5 }, new int[] { }, false, new int[] { 1, 2, 3, 4, 5 } }, - { new int[] { 2, 3, 4, 5 }, new int[] { 1 }, true, new int[] { 1, 2, 3, 4, 5 } }, - { new int[] { 3, 4, 5 }, new int[] { 1, 2 }, true, new int[] { 1, 2, 3, 4, 5 } }, + { Array.Empty(), Array.Empty(), false, Array.Empty() }, + { Array.Empty(), new[] { 1 }, true, new[] { 1 } }, + { Array.Empty(), new[] { 1, 2, 3, 4, 5 }, true, new[] { 1, 2, 3, 4, 5 } }, + { new[] { 1 }, Array.Empty(), false, new[] { 1 } }, + { new[] { 2 }, new[] { 1 }, true, new[] { 1, 2 } }, + { new[] { 5 }, new[] { 1, 2, 3, 4 }, true, new[] { 1, 2, 3, 4, 5 } }, + { new[] { 1, 2, 3, 4, 5 }, Array.Empty(), false, new[] { 1, 2, 3, 4, 5 } }, + { new[] { 2, 3, 4, 5 }, new[] { 1 }, true, new[] { 1, 2, 3, 4, 5 } }, + { new[] { 3, 4, 5 }, new[] { 1, 2 }, true, new[] { 1, 2, 3, 4, 5 } }, }; [Theory] @@ -116,10 +116,10 @@ void AddEnumerable(IReadOnlyList collection, IEnumerable items, bool i list.Version.Must().BeNotEqualTo(version); else list.Version.Must().BeEqualTo(version); - list.EnumerateForward().Must() + list.Forward.Must() .BeEnumerableOf() .BeEqualTo(expected); - list.EnumerateReversed().Must() + list.Backward.Must() .BeEnumerableOf() .BeEqualTo(expected.Reverse()); } @@ -140,35 +140,35 @@ void AddCollection(IReadOnlyList collection, IReadOnlyList items, bool list.Version.Must().BeNotEqualTo(version); else list.Version.Must().BeEqualTo(version); - list.EnumerateForward().Must() + list.Forward.Must() .BeEnumerableOf() .BeEqualTo(expected); - list.EnumerateReversed().Must() + list.Backward.Must() .BeEnumerableOf() .BeEqualTo(expected.Reverse()); } public static TheoryData, IReadOnlyList, bool, bool, IReadOnlyList> ListData => - new TheoryData, IReadOnlyList, bool, bool, IReadOnlyList> + new() { - { new int[] { }, new int[] { }, false, false, new int[] { } }, - { new int[] { }, new int[] { 1 }, false, true, new int[] { 1 } }, - { new int[] { }, new int[] { 1, 2, 3, 4, 5 }, false, true, new int[] { 1, 2, 3, 4, 5 } }, - { new int[] { 1 }, new int[] { }, false, false, new int[] { 1 } }, - { new int[] { 2 }, new int[] { 1 }, false, true, new int[] { 1, 2 } }, - { new int[] { 5 }, new int[] { 1, 2, 3, 4 }, false, true, new int[] { 1, 2, 3, 4, 5 } }, - { new int[] { 1, 2, 3, 4, 5 }, new int[] { }, false, false, new int[] { 1, 2, 3, 4, 5 } }, - { new int[] { 2, 3, 4, 5 }, new int[] { 1 }, false, true, new int[] { 1, 2, 3, 4, 5 } }, - { new int[] { 3, 4, 5 }, new int[] { 1, 2 }, false, true, new int[] { 1, 2, 3, 4, 5 } }, - { new int[] { }, new int[] { }, true, false, new int[] { } }, - { new int[] { }, new int[] { 1 }, true, true, new int[] { 1 } }, - { new int[] { }, new int[] { 1, 2, 3, 4, 5 }, true, true, new int[] { 5, 4, 3, 2, 1 } }, - { new int[] { 1 }, new int[] { }, true, false, new int[] { 1 } }, - { new int[] { 2 }, new int[] { 1 }, true, true, new int[] { 1, 2 } }, - { new int[] { 5 }, new int[] { 1, 2, 3, 4 }, true, true, new int[] { 4, 3, 2, 1, 5 } }, - { new int[] { 1, 2, 3, 4, 5 }, new int[] { }, true, false, new int[] { 1, 2, 3, 4, 5 } }, - { new int[] { 2, 3, 4, 5 }, new int[] { 1 }, true, true, new int[] { 1, 2, 3, 4, 5 } }, - { new int[] { 3, 4, 5 }, new int[] { 1, 2 }, true, true, new int[] { 2, 1, 3, 4, 5 } }, + { Array.Empty(), Array.Empty(), false, false, Array.Empty() }, + { Array.Empty(), new[] { 1 }, false, true, new[] { 1 } }, + { Array.Empty(), new[] { 1, 2, 3, 4, 5 }, false, true, new[] { 1, 2, 3, 4, 5 } }, + { new[] { 1 }, Array.Empty(), false, false, new[] { 1 } }, + { new[] { 2 }, new[] { 1 }, false, true, new[] { 1, 2 } }, + { new[] { 5 }, new[] { 1, 2, 3, 4 }, false, true, new[] { 1, 2, 3, 4, 5 } }, + { new[] { 1, 2, 3, 4, 5 }, Array.Empty(), false, false, new[] { 1, 2, 3, 4, 5 } }, + { new[] { 2, 3, 4, 5 }, new[] { 1 }, false, true, new[] { 1, 2, 3, 4, 5 } }, + { new[] { 3, 4, 5 }, new[] { 1, 2 }, false, true, new[] { 1, 2, 3, 4, 5 } }, + { Array.Empty(), Array.Empty(), true, false, Array.Empty() }, + { Array.Empty(), new[] { 1 }, true, true, new[] { 1 } }, + { Array.Empty(), new[] { 1, 2, 3, 4, 5 }, true, true, new[] { 5, 4, 3, 2, 1 } }, + { new[] { 1 }, Array.Empty(), true, false, new[] { 1 } }, + { new[] { 2 }, new[] { 1 }, true, true, new[] { 1, 2 } }, + { new[] { 5 }, new[] { 1, 2, 3, 4 }, true, true, new[] { 4, 3, 2, 1, 5 } }, + { new[] { 1, 2, 3, 4, 5 }, Array.Empty(), true, false, new[] { 1, 2, 3, 4, 5 } }, + { new[] { 2, 3, 4, 5 }, new[] { 1 }, true, true, new[] { 1, 2, 3, 4, 5 } }, + { new[] { 3, 4, 5 }, new[] { 1, 2 }, true, true, new[] { 2, 1, 3, 4, 5 } }, }; [Theory] @@ -188,10 +188,10 @@ void AddList(IReadOnlyList collection, IReadOnlyList items, bool rever left.Version.Must().BeNotEqualTo(version); else left.Version.Must().BeEqualTo(version); - left.EnumerateForward().Must() + left.Forward.Must() .BeEnumerableOf() .BeEqualTo(expected); - left.EnumerateReversed().Must() + left.Backward.Must() .BeEnumerableOf() .BeEqualTo(expected.Reverse()); } @@ -213,13 +213,13 @@ void AddListFrom(IReadOnlyList collection, IReadOnlyList items, bool r left.Version.Must().BeNotEqualTo(version); else left.Version.Must().BeEqualTo(version); - left.EnumerateForward().Must() + left.Forward.Must() .BeEnumerableOf() .BeEqualTo(expected); - left.EnumerateReversed().Must() + left.Backward.Must() .BeEnumerableOf() .BeEqualTo(expected.Reverse()); - right.EnumerateForward().Must() + right.Forward.Must() .BeEnumerableOf() .BeEmpty(); } diff --git a/NetFabric.DoublyLinkedList.Tests/AddLastTests.cs b/NetFabric.DoublyLinkedList.Tests/AddLastTests.cs index eb2691f..e8846dd 100644 --- a/NetFabric.DoublyLinkedList.Tests/AddLastTests.cs +++ b/NetFabric.DoublyLinkedList.Tests/AddLastTests.cs @@ -57,11 +57,11 @@ void AddNullList() } public static TheoryData, int, IReadOnlyList> ItemData => - new TheoryData, int, IReadOnlyList> + new() { - { new int[] { }, 1, new int[] { 1 } }, - { new int[] { 1 }, 2, new int[] { 1, 2 } }, - { new int[] { 1, 2, 3, 4 }, 5, new int[] { 1, 2, 3, 4, 5 } }, + { Array.Empty(), 1, new[] { 1 } }, + { new[] { 1 }, 2, new[] { 1, 2 } }, + { new[] { 1, 2, 3, 4 }, 5, new[] { 1, 2, 3, 4, 5 } }, }; [Theory] @@ -78,26 +78,26 @@ void AddItem(IReadOnlyList collection, int item, IReadOnlyList expecte // Assert list.Version.Must() .BeNotEqualTo(version); - list.EnumerateForward().Must() + list.Forward.Must() .BeEnumerableOf() .BeEqualTo(expected); - list.EnumerateReversed().Must() + list.Backward.Must() .BeEnumerableOf() .BeEqualTo(expected.Reverse()); } public static TheoryData, IReadOnlyList, bool, IReadOnlyList> CollectionData => - new TheoryData, IReadOnlyList, bool, IReadOnlyList> + new() { - { new int[] { }, new int[] { }, false, new int[] { } }, - { new int[] { }, new int[] { 1 }, true, new int[] { 1 } }, - { new int[] { }, new int[] { 1, 2, 3, 4, 5 }, true, new int[] { 1, 2, 3, 4, 5 } }, - { new int[] { 1 }, new int[] { }, false, new int[] { 1 } }, - { new int[] { 1 }, new int[] { 2 }, true, new int[] { 1, 2 } }, - { new int[] { 1 }, new int[] { 2, 3, 4, 5 }, true, new int[] { 1, 2, 3, 4, 5 } }, - { new int[] { 1, 2, 3, 4, 5 }, new int[] { }, false, new int[] { 1, 2, 3, 4, 5 } }, - { new int[] { 1, 2, 3, 4 }, new int[] { 5 }, true, new int[] { 1, 2, 3, 4, 5 } }, - { new int[] { 1, 2, 3 }, new int[] { 4, 5 }, true, new int[] { 1, 2, 3, 4, 5 } }, + { Array.Empty(), Array.Empty(), false, Array.Empty() }, + { Array.Empty(), new[] { 1 }, true, new[] { 1 } }, + { Array.Empty(), new[] { 1, 2, 3, 4, 5 }, true, new[] { 1, 2, 3, 4, 5 } }, + { new[] { 1 }, Array.Empty(), false, new[] { 1 } }, + { new[] { 1 }, new[] { 2 }, true, new[] { 1, 2 } }, + { new[] { 1 }, new[] { 2, 3, 4, 5 }, true, new[] { 1, 2, 3, 4, 5 } }, + { new[] { 1, 2, 3, 4, 5 }, Array.Empty(), false, new[] { 1, 2, 3, 4, 5 } }, + { new[] { 1, 2, 3, 4 }, new[] { 5 }, true, new[] { 1, 2, 3, 4, 5 } }, + { new[] { 1, 2, 3 }, new[] { 4, 5 }, true, new[] { 1, 2, 3, 4, 5 } }, }; [Theory] @@ -116,10 +116,10 @@ void AddEnumerable(IReadOnlyList collection, IEnumerable items, bool i list.Version.Must().BeNotEqualTo(version); else list.Version.Must().BeEqualTo(version); - list.EnumerateForward().Must() + list.Forward.Must() .BeEnumerableOf() .BeEqualTo(expected); - list.EnumerateReversed().Must() + list.Backward.Must() .BeEnumerableOf() .BeEqualTo(expected.Reverse()); } @@ -140,35 +140,35 @@ void AddCollection(IReadOnlyList collection, IReadOnlyList items, bool list.Version.Must().BeNotEqualTo(version); else list.Version.Must().BeEqualTo(version); - list.EnumerateForward().Must() + list.Forward.Must() .BeEnumerableOf() .BeEqualTo(expected); - list.EnumerateReversed().Must() + list.Backward.Must() .BeEnumerableOf() .BeEqualTo(expected.Reverse()); } public static TheoryData, IReadOnlyList, bool, bool, IReadOnlyList> ListData => - new TheoryData, IReadOnlyList, bool, bool, IReadOnlyList> + new() { - { new int[] { }, new int[] { }, false, false, new int[] { } }, - { new int[] { }, new int[] { 1 }, false, true, new int[] { 1 } }, - { new int[] { }, new int[] { 1, 2, 3, 4, 5 }, false, true, new int[] { 1, 2, 3, 4, 5 } }, - { new int[] { 1 }, new int[] { }, false, false, new int[] { 1 } }, - { new int[] { 1 }, new int[] { 2 }, false, true, new int[] { 1, 2 } }, - { new int[] { 1 }, new int[] { 2, 3, 4, 5 }, false, true, new int[] { 1, 2, 3, 4, 5 } }, - { new int[] { 1, 2, 3, 4, 5 }, new int[] { }, false, false, new int[] { 1, 2, 3, 4, 5 } }, - { new int[] { 1, 2, 3, 4 }, new int[] { 5 }, false, true, new int[] { 1, 2, 3, 4, 5 } }, - { new int[] { 1, 2, 3 }, new int[] { 4, 5 }, false, true, new int[] { 1, 2, 3, 4, 5 } }, - { new int[] { }, new int[] { }, true, false, new int[] { } }, - { new int[] { }, new int[] { 1 }, true, true, new int[] { 1 } }, - { new int[] { }, new int[] { 1, 2, 3, 4, 5 }, true, true, new int[] { 5, 4, 3, 2, 1 } }, - { new int[] { 1 }, new int[] { }, true, false, new int[] { 1 } }, - { new int[] { 1 }, new int[] { 2 }, true, true, new int[] { 1, 2 } }, - { new int[] { 1 }, new int[] { 2, 3, 4, 5 }, true, true, new int[] { 1, 5, 4, 3, 2 } }, - { new int[] { 1, 2, 3, 4, 5 }, new int[] { }, true, false, new int[] { 1, 2, 3, 4, 5 } }, - { new int[] { 1, 2, 3, 4 }, new int[] { 5 }, true, true, new int[] { 1, 2, 3, 4, 5 } }, - { new int[] { 1, 2, 3 }, new int[] { 4, 5 }, true, true, new int[] { 1, 2, 3, 5, 4 } }, + { Array.Empty(), Array.Empty(), false, false, Array.Empty() }, + { Array.Empty(), new[] { 1 }, false, true, new[] { 1 } }, + { Array.Empty(), new[] { 1, 2, 3, 4, 5 }, false, true, new[] { 1, 2, 3, 4, 5 } }, + { new[] { 1 }, Array.Empty(), false, false, new[] { 1 } }, + { new[] { 1 }, new[] { 2 }, false, true, new[] { 1, 2 } }, + { new[] { 1 }, new[] { 2, 3, 4, 5 }, false, true, new[] { 1, 2, 3, 4, 5 } }, + { new[] { 1, 2, 3, 4, 5 }, Array.Empty(), false, false, new[] { 1, 2, 3, 4, 5 } }, + { new[] { 1, 2, 3, 4 }, new[] { 5 }, false, true, new[] { 1, 2, 3, 4, 5 } }, + { new[] { 1, 2, 3 }, new[] { 4, 5 }, false, true, new[] { 1, 2, 3, 4, 5 } }, + { Array.Empty(), Array.Empty(), true, false, Array.Empty() }, + { Array.Empty(), new[] { 1 }, true, true, new[] { 1 } }, + { Array.Empty(), new[] { 1, 2, 3, 4, 5 }, true, true, new[] { 5, 4, 3, 2, 1 } }, + { new[] { 1 }, Array.Empty(), true, false, new[] { 1 } }, + { new[] { 1 }, new[] { 2 }, true, true, new[] { 1, 2 } }, + { new[] { 1 }, new[] { 2, 3, 4, 5 }, true, true, new[] { 1, 5, 4, 3, 2 } }, + { new[] { 1, 2, 3, 4, 5 }, Array.Empty(), true, false, new[] { 1, 2, 3, 4, 5 } }, + { new[] { 1, 2, 3, 4 }, new[] { 5 }, true, true, new[] { 1, 2, 3, 4, 5 } }, + { new[] { 1, 2, 3 }, new[] { 4, 5 }, true, true, new[] { 1, 2, 3, 5, 4 } }, }; [Theory] @@ -188,10 +188,10 @@ void AddList(IReadOnlyList collection, IReadOnlyList items, bool rever left.Version.Must().BeNotEqualTo(version); else left.Version.Must().BeEqualTo(version); - left.EnumerateForward().Must() + left.Forward.Must() .BeEnumerableOf() .BeEqualTo(expected); - left.EnumerateReversed().Must() + left.Backward.Must() .BeEnumerableOf() .BeEqualTo(expected.Reverse()); } @@ -213,13 +213,13 @@ void AddListFrom(IReadOnlyList collection, IReadOnlyList items, bool r left.Version.Must().BeNotEqualTo(version); else left.Version.Must().BeEqualTo(version); - left.EnumerateForward().Must() + left.Forward.Must() .BeEnumerableOf() .BeEqualTo(expected); - left.EnumerateReversed().Must() + left.Backward.Must() .BeEnumerableOf() .BeEqualTo(expected.Reverse()); - right.EnumerateForward().Must() + right.Forward.Must() .BeEnumerableOf() .BeEmpty(); } diff --git a/NetFabric.DoublyLinkedList.Tests/AppendTests.cs b/NetFabric.DoublyLinkedList.Tests/AppendTests.cs index 0518f55..49d85f0 100644 --- a/NetFabric.DoublyLinkedList.Tests/AppendTests.cs +++ b/NetFabric.DoublyLinkedList.Tests/AppendTests.cs @@ -39,16 +39,16 @@ void AppendNullRight() } public static TheoryData, IReadOnlyList, IReadOnlyList> AppendData => - new TheoryData, IReadOnlyList, IReadOnlyList> + new() { - { new int[] { }, new int[] { }, new int[] { } }, - { new int[] { }, new int[] { 1 } , new int[] { 1 } }, - { new int[] { 1 }, new int[] { }, new int[] { 1 } }, - { new int[] { }, new int[] { 1, 2, 3, 4, 5 } , new int[] { 1, 2, 3, 4, 5 } }, - { new int[] { 1, 2, 3, 4, 5 }, new int[] { }, new int[] { 1, 2, 3, 4, 5 } }, - { new int[] { 1 }, new int[] { 2, 3, 4, 5 }, new int[] { 1, 2, 3, 4, 5 } }, - { new int[] { 1, 2, 3, 4 }, new int[] { 5 } , new int[] { 1, 2, 3, 4, 5 } }, - { new int[] { 1, 2, 3 }, new int[] { 4, 5, 6 }, new int[] { 1, 2, 3, 4, 5, 6 } }, + { Array.Empty(), Array.Empty(), Array.Empty() }, + { Array.Empty(), new[] { 1 } , new[] { 1 } }, + { new[] { 1 }, Array.Empty(), new[] { 1 } }, + { Array.Empty(), new[] { 1, 2, 3, 4, 5 } , new[] { 1, 2, 3, 4, 5 } }, + { new[] { 1, 2, 3, 4, 5 }, Array.Empty(), new[] { 1, 2, 3, 4, 5 } }, + { new[] { 1 }, new[] { 2, 3, 4, 5 }, new[] { 1, 2, 3, 4, 5 } }, + { new[] { 1, 2, 3, 4 }, new[] { 5 } , new[] { 1, 2, 3, 4, 5 } }, + { new[] { 1, 2, 3 }, new[] { 4, 5, 6 }, new[] { 1, 2, 3, 4, 5, 6 } }, }; [Theory] @@ -67,20 +67,20 @@ void Append(IReadOnlyList left, IReadOnlyList right, IReadOnlyCollecti // Assert leftList.Version.Must() .BeEqualTo(leftVersion); - leftList.EnumerateForward().Must() + leftList.Forward.Must() .BeEnumerableOf() .BeEqualTo(left); rightList.Version.Must() .BeEqualTo(rightVersion); - rightList.EnumerateForward().Must() + rightList.Forward.Must() .BeEnumerableOf() .BeEqualTo(right); - result.EnumerateForward().Must() + result.Forward.Must() .BeEnumerableOf() .BeEqualTo(expected); - result.EnumerateReversed().Must() + result.Backward.Must() .BeEnumerableOf() .BeEqualTo(expected.Reverse()); } diff --git a/NetFabric.DoublyLinkedList.Tests/ClearTests.cs b/NetFabric.DoublyLinkedList.Tests/ClearTests.cs index 0724bc5..0c31eec 100644 --- a/NetFabric.DoublyLinkedList.Tests/ClearTests.cs +++ b/NetFabric.DoublyLinkedList.Tests/ClearTests.cs @@ -8,11 +8,11 @@ namespace NetFabric.Tests public class ClearTests { public static TheoryData> Data => - new TheoryData> + new() { - new int[] { }, - new int[] { 1 }, - new int[] { 1, 2, 3, 4, 5 }, + Array.Empty(), + new[] { 1 }, + new[] { 1, 2, 3, 4, 5 }, }; [Theory] @@ -29,10 +29,10 @@ public void Clear(IReadOnlyList collection) // Assert list.Version.Must() .BeNotEqualTo(version); - list.EnumerateForward().Must() + list.Forward.Must() .BeEnumerableOf() .BeEmpty(); - list.EnumerateReversed().Must() + list.Backward.Must() .BeEnumerableOf() .BeEmpty(); } diff --git a/NetFabric.DoublyLinkedList.Tests/CloneTests.cs b/NetFabric.DoublyLinkedList.Tests/CloneTests.cs index dc3ea70..124274a 100644 --- a/NetFabric.DoublyLinkedList.Tests/CloneTests.cs +++ b/NetFabric.DoublyLinkedList.Tests/CloneTests.cs @@ -9,11 +9,11 @@ namespace NetFabric.Tests public class CloneTests { public static TheoryData, IReadOnlyList> Data => - new TheoryData, IReadOnlyList> + new() { - { new int[] { }, new int[] { } }, - { new int[] { 1 }, new int[] { 1 } }, - { new int[] { 1, 2, 3, 4, 5 }, new int[] { 1, 2, 3, 4, 5 } }, + { Array.Empty(), Array.Empty() }, + { new[] { 1 }, new[] { 1 } }, + { new[] { 1, 2, 3, 4, 5 }, new[] { 1, 2, 3, 4, 5 } }, }; [Theory] @@ -29,10 +29,10 @@ public void Clone(IReadOnlyList collection, IReadOnlyList expected) // Assert result.Version.Must() .BeEqualTo(0); - result.EnumerateForward().Must() + result.Forward.Must() .BeEnumerableOf() .BeEqualTo(expected); - result.EnumerateReversed().Must() + result.Backward.Must() .BeEnumerableOf() .BeEqualTo(collection.Reverse()); } diff --git a/NetFabric.DoublyLinkedList.Tests/FindLastTests.cs b/NetFabric.DoublyLinkedList.Tests/FindLastTests.cs index f2a84ca..3bf68ce 100644 --- a/NetFabric.DoublyLinkedList.Tests/FindLastTests.cs +++ b/NetFabric.DoublyLinkedList.Tests/FindLastTests.cs @@ -8,7 +8,7 @@ namespace NetFabric.Tests public class FindLastTests { public static TheoryData, int?, bool> Data => - new TheoryData, int?, bool> + new() { { new int?[] { }, null, false }, { new int?[] { }, 1, false }, @@ -50,7 +50,7 @@ public void FindLast(IReadOnlyList collection, int? value, bool shouldFind } public static TheoryData, int?> TailData => - new TheoryData, int?> + new() { { new int?[] { null, null, null }, null }, { new int?[] { 1, 1, 1 }, 1 }, diff --git a/NetFabric.DoublyLinkedList.Tests/FindTests.cs b/NetFabric.DoublyLinkedList.Tests/FindTests.cs index 2fae632..fb6c2ba 100644 --- a/NetFabric.DoublyLinkedList.Tests/FindTests.cs +++ b/NetFabric.DoublyLinkedList.Tests/FindTests.cs @@ -8,7 +8,7 @@ namespace NetFabric.Tests public class FindTests { public static TheoryData, int?, bool> Data => - new TheoryData, int?, bool> + new() { { new int?[] { }, null, false }, { new int?[] { }, 1, false }, @@ -50,7 +50,7 @@ public void Find(IReadOnlyList collection, int? value, bool shouldFind) } public static TheoryData, int?> HeadData => - new TheoryData, int?> + new() { { new int?[] { null, null, null }, null }, { new int?[] { 1, 1, 1 }, 1 }, diff --git a/NetFabric.DoublyLinkedList.Tests/ForwardEnumerationTests.cs b/NetFabric.DoublyLinkedList.Tests/ForwardEnumerationTests.cs index 1f417cd..ba3dac7 100644 --- a/NetFabric.DoublyLinkedList.Tests/ForwardEnumerationTests.cs +++ b/NetFabric.DoublyLinkedList.Tests/ForwardEnumerationTests.cs @@ -8,11 +8,11 @@ namespace NetFabric.Tests public class ForwardEnumerationTests { public static TheoryData> Data => - new TheoryData> + new() { - new int[] { }, - new int[] { 1 }, - new int[] { 1, 2, 3, 4, 5 }, + Array.Empty(), + new[] { 1 }, + new[] { 1, 2, 3, 4, 5 }, }; [Theory] @@ -23,7 +23,7 @@ public void Enumeration(IReadOnlyList collection) var list = new DoublyLinkedList(collection); // Act - var enumeration = list.EnumerateForward(); + var enumeration = list.Forward; // Assert enumeration.Must() diff --git a/NetFabric.DoublyLinkedList.Tests/NetFabric.DoublyLinkedList.Tests.csproj b/NetFabric.DoublyLinkedList.Tests/NetFabric.DoublyLinkedList.Tests.csproj index 92c2e2c..844a2b8 100644 --- a/NetFabric.DoublyLinkedList.Tests/NetFabric.DoublyLinkedList.Tests.csproj +++ b/NetFabric.DoublyLinkedList.Tests/NetFabric.DoublyLinkedList.Tests.csproj @@ -6,7 +6,7 @@ - + diff --git a/NetFabric.DoublyLinkedList.Tests/RemoveTests.cs b/NetFabric.DoublyLinkedList.Tests/RemoveTests.cs index 416cb6d..99dd02c 100644 --- a/NetFabric.DoublyLinkedList.Tests/RemoveTests.cs +++ b/NetFabric.DoublyLinkedList.Tests/RemoveTests.cs @@ -9,27 +9,27 @@ namespace NetFabric.Tests public class RemoveTests { public static TheoryData, int, bool, IReadOnlyCollection> RemoveItemData => - new TheoryData, int, bool, IReadOnlyCollection> + new() { - { new int[] { }, 1, false, new int[] { } }, - { new int[] { 1 }, 2, false, new int[] { 1 } }, - { new int[] { 1 }, 1, true, new int[] { } }, - { new int[] { 1, 2, 3, 4, 5 }, 6, false, new int[] { 1, 2, 3, 4, 5 } }, - { new int[] { 1, 2, 3, 4, 5 }, 1, true, new int[] { 2, 3, 4, 5 } }, - { new int[] { 1, 2, 3, 4, 5 }, 3, true, new int[] { 1, 2, 4, 5 } }, - { new int[] { 1, 2, 3, 4, 5 }, 5, true, new int[] { 1, 2, 3, 4 } }, + { Array.Empty(), 1, false, Array.Empty() }, + { new[] { 1 }, 2, false, new[] { 1 } }, + { new[] { 1 }, 1, true, Array.Empty() }, + { new[] { 1, 2, 3, 4, 5 }, 6, false, new[] { 1, 2, 3, 4, 5 } }, + { new[] { 1, 2, 3, 4, 5 }, 1, true, new[] { 2, 3, 4, 5 } }, + { new[] { 1, 2, 3, 4, 5 }, 3, true, new[] { 1, 2, 4, 5 } }, + { new[] { 1, 2, 3, 4, 5 }, 5, true, new[] { 1, 2, 3, 4 } }, }; public static TheoryData, int, bool, IReadOnlyCollection> RemoveFirstItemData => - new TheoryData, int, bool, IReadOnlyCollection> + new() { - { new int[] { 1, 2, 3, 4, 5, 1 }, 1, true, new int[] { 2, 3, 4, 5, 1 } }, + { new[] { 1, 2, 3, 4, 5, 1 }, 1, true, new[] { 2, 3, 4, 5, 1 } }, }; public static TheoryData, int, bool, IReadOnlyCollection> RemoveLastItemData => - new TheoryData, int, bool, IReadOnlyCollection> + new() { - { new int[] { 1, 2, 3, 4, 5, 1 }, 1, true, new int[] { 1, 2, 3, 4, 5 } }, + { new[] { 1, 2, 3, 4, 5, 1 }, 1, true, new[] { 1, 2, 3, 4, 5 } }, }; [Theory] @@ -50,10 +50,10 @@ void RemoveItem(IReadOnlyList collection, int item, bool expected, IReadOnl list.Version.Must().BeNotEqualTo(version); else list.Version.Must().BeEqualTo(version); - list.EnumerateForward().Must() + list.Forward.Must() .BeEnumerableOf() .BeEqualTo(expectedCollection); - list.EnumerateReversed().Must() + list.Backward.Must() .BeEnumerableOf() .BeEqualTo(expectedCollection.Reverse()); } @@ -77,26 +77,26 @@ void RemoveLastItem(IReadOnlyList collection, int item, bool expected, IRea list.Version.Must().BeNotEqualTo(version); else list.Version.Must().BeEqualTo(version); - list.EnumerateForward().Must() + list.Forward.Must() .BeEnumerableOf() .BeEqualTo(expectedCollection); - list.EnumerateReversed().Must() + list.Backward.Must() .BeEnumerableOf() .BeEqualTo(expectedCollection.Reverse()); } public static TheoryData, IReadOnlyCollection> RemoveFirstData => - new TheoryData, IReadOnlyCollection> + new() { - { new int[] { 1 }, new int[] { } }, - { new int[] { 1, 2, 3, 4, 5 }, new int[] { 2, 3, 4, 5 } }, + { new[] { 1 }, Array.Empty() }, + { new[] { 1, 2, 3, 4, 5 }, new[] { 2, 3, 4, 5 } }, }; public static TheoryData, IReadOnlyCollection> RemoveLastData => - new TheoryData, IReadOnlyCollection> + new() { - { new int[] { 1 }, new int[] { } }, - { new int[] { 1, 2, 3, 4, 5 }, new int[] { 1, 2, 3, 4 } }, + { new[] { 1 }, Array.Empty() }, + { new[] { 1, 2, 3, 4, 5 }, new[] { 1, 2, 3, 4 } }, }; [Theory] @@ -113,10 +113,10 @@ void RemoveFirst(IReadOnlyList collection, IReadOnlyCollection expecte // Assert list.Version.Must() .BeNotEqualTo(version); - list.EnumerateForward().Must() + list.Forward.Must() .BeEnumerableOf() .BeEqualTo(expectedCollection); - list.EnumerateReversed().Must() + list.Backward.Must() .BeEnumerableOf() .BeEqualTo(expectedCollection.Reverse()); } @@ -135,10 +135,10 @@ void RemoveLast(IReadOnlyList collection, IReadOnlyCollection expected // Assert list.Version.Must() .BeNotEqualTo(version); - list.EnumerateForward().Must() + list.Forward.Must() .BeEnumerableOf() .BeEqualTo(expectedCollection); - list.EnumerateReversed().Must() + list.Backward.Must() .BeEnumerableOf() .BeEqualTo(expectedCollection.Reverse()); } diff --git a/NetFabric.DoublyLinkedList.Tests/ReverseEnumerationTests.cs b/NetFabric.DoublyLinkedList.Tests/ReverseEnumerationTests.cs index 1582215..83d1a18 100644 --- a/NetFabric.DoublyLinkedList.Tests/ReverseEnumerationTests.cs +++ b/NetFabric.DoublyLinkedList.Tests/ReverseEnumerationTests.cs @@ -8,11 +8,11 @@ namespace NetFabric.Tests public class ReverseEnumerationTests { public static TheoryData, IReadOnlyList> Data => - new TheoryData, IReadOnlyList> + new() { - { new int[] { }, new int[] { } }, - { new int[] { 1 }, new int[] { 1 } }, - { new int[] { 1, 2, 3, 4, 5 }, new int[] { 5, 4, 3, 2, 1 } }, + { Array.Empty(), Array.Empty() }, + { new[] { 1 }, new[] { 1 } }, + { new[] { 1, 2, 3, 4, 5 }, new[] { 5, 4, 3, 2, 1 } }, }; [Theory] @@ -23,7 +23,7 @@ public void Enumeration(IReadOnlyList collection, IReadOnlyList expect var list = new DoublyLinkedList(collection); // Act - var enumeration = list.EnumerateReversed(); + var enumeration = list.Backward; // Assert enumeration.Must() diff --git a/NetFabric.DoublyLinkedList.Tests/ReverseTests.cs b/NetFabric.DoublyLinkedList.Tests/ReverseTests.cs index dc021aa..52e1851 100644 --- a/NetFabric.DoublyLinkedList.Tests/ReverseTests.cs +++ b/NetFabric.DoublyLinkedList.Tests/ReverseTests.cs @@ -8,11 +8,11 @@ namespace NetFabric.Tests public class ReverseTests { public static TheoryData, IReadOnlyList> Data => - new TheoryData, IReadOnlyList> + new() { - { new int[] { }, new int[] { } }, - { new int[] { 1 }, new int[] { 1 } }, - { new int[] { 1, 2, 3, 4, 5 }, new int[] { 5, 4, 3, 2, 1 } }, + { Array.Empty(), Array.Empty() }, + { new[] { 1 }, new[] { 1 } }, + { new[] { 1, 2, 3, 4, 5 }, new[] { 5, 4, 3, 2, 1 } }, }; [Theory] @@ -28,10 +28,10 @@ public void Reverse(IReadOnlyList collection, IReadOnlyList expected) // Assert result.Version.Must() .BeEqualTo(0); - result.EnumerateForward().Must() + result.Forward.Must() .BeEnumerableOf() .BeEqualTo(expected); - result.EnumerateReversed().Must() + result.Backward.Must() .BeEnumerableOf() .BeEqualTo(collection); } @@ -52,10 +52,10 @@ public void ReverseInPlace(IReadOnlyList collection, IReadOnlyList exp list.Version.Must().BeEqualTo(version); else list.Version.Must().BeNotEqualTo(version); - list.EnumerateForward().Must() + list.Forward.Must() .BeEnumerableOf() .BeEqualTo(expected); - list.EnumerateReversed().Must() + list.Backward.Must() .BeEnumerableOf() .BeEqualTo(collection); } diff --git a/NetFabric.DoublyLinkedList/DoublyLinkedList'1.ForwardEnumeration.cs b/NetFabric.DoublyLinkedList/DoublyLinkedList'1.ForwardEnumeration.cs index 921ded6..a793f72 100644 --- a/NetFabric.DoublyLinkedList/DoublyLinkedList'1.ForwardEnumeration.cs +++ b/NetFabric.DoublyLinkedList/DoublyLinkedList'1.ForwardEnumeration.cs @@ -2,6 +2,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; @@ -9,121 +10,103 @@ namespace NetFabric { public partial class DoublyLinkedList { - public readonly struct ForwardEnumeration : IValueReadOnlyCollection + public readonly struct ForwardEnumeration + : IValueReadOnlyList { readonly DoublyLinkedList list; internal ForwardEnumeration(DoublyLinkedList list) - { - this.list = list; - } + => this.list = list; public int Count => list.count; + + public T this[int index] + { + get + { + if ((uint)index >= (uint)Count) + Throw.ArgumentOutOfRangeException(nameof(index)); + return index < Count / 2 + ? ForwardOffset(list.First, index)!.Value + : ReverseOffset(list.Last, Count - index - 1)!.Value; + } + } [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly Enumerator GetEnumerator() => - new Enumerator(list); + public Enumerator GetEnumerator() => + new(list); - readonly DisposableEnumerator IValueEnumerable.GetEnumerator() => - new DisposableEnumerator(list); + DisposableEnumerator IValueEnumerable.GetEnumerator() => + new(list); - readonly IEnumerator IEnumerable.GetEnumerator() => + IEnumerator IEnumerable.GetEnumerator() => new DisposableEnumerator(list); - readonly IEnumerator IEnumerable.GetEnumerator() => + IEnumerator IEnumerable.GetEnumerator() => new DisposableEnumerator(list); public struct Enumerator { readonly DoublyLinkedList list; readonly int version; - Node current; - EnumeratorState state; + Node? current; internal Enumerator(DoublyLinkedList list) { this.list = list; version = list.version; current = null; - state = list.IsEmpty ? EnumeratorState.Empty : EnumeratorState.First; } public readonly T Current - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => current.Value; - } + => current!.Value; public bool MoveNext() { - if (version != list.version) - ThrowInvalidOperation(); - - switch (state) - { - case EnumeratorState.Normal: - current = current.Next; - return current is object; - case EnumeratorState.First: - current = list.First; - state = EnumeratorState.Normal; - return true; - default: - return false; - } - - static void ThrowInvalidOperation() => throw new InvalidOperationException(); + if (version != list.version) + Throw.InvalidOperationException(); + current = current is null + ? list.First + : current.Next; + return current is not null; } } - public struct DisposableEnumerator : IEnumerator + public struct DisposableEnumerator + : IEnumerator { readonly DoublyLinkedList list; readonly int version; - Node current; - EnumeratorState state; + Node? current; internal DisposableEnumerator(DoublyLinkedList list) { this.list = list; version = list.version; current = null; - state = list.IsEmpty ? EnumeratorState.Empty : EnumeratorState.First; } public readonly T Current - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => current.Value; - } - readonly object IEnumerator.Current => current.Value; + => current!.Value; + readonly object? IEnumerator.Current + => current!.Value; public bool MoveNext() { - if (version != list.version) - ThrowInvalidOperation(); - - switch (state) - { - case EnumeratorState.Normal: - current = current.Next; - return current is object; - case EnumeratorState.First: - current = list.First; - state = EnumeratorState.Normal; - return true; - default: - return false; - } - - static void ThrowInvalidOperation() => throw new InvalidOperationException(); + if (version != list.version) Throw.InvalidOperationException(); + current = current is null + ? list.First + : current.Next; + return current is not null; } - public readonly void Reset() => throw new NotSupportedException(); + public readonly void Reset() + => Throw.NotSupportedException(); - public readonly void Dispose() { } + public readonly void Dispose() + { } } } } diff --git a/NetFabric.DoublyLinkedList/DoublyLinkedList'1.Node.cs b/NetFabric.DoublyLinkedList/DoublyLinkedList'1.Node.cs index 3f52348..5d72d13 100644 --- a/NetFabric.DoublyLinkedList/DoublyLinkedList'1.Node.cs +++ b/NetFabric.DoublyLinkedList/DoublyLinkedList'1.Node.cs @@ -4,11 +4,11 @@ public partial class DoublyLinkedList { public sealed class Node { - public DoublyLinkedList List { get; internal set; } + public DoublyLinkedList? List { get; internal set; } - public Node Next { get; internal set; } + public Node? Next { get; internal set; } - public Node Previous { get; internal set; } + public Node? Previous { get; internal set; } public T Value { get; set; } diff --git a/NetFabric.DoublyLinkedList/DoublyLinkedList'1.ReverseEnumeration.cs b/NetFabric.DoublyLinkedList/DoublyLinkedList'1.ReverseEnumeration.cs index e569512..1246e9d 100644 --- a/NetFabric.DoublyLinkedList/DoublyLinkedList'1.ReverseEnumeration.cs +++ b/NetFabric.DoublyLinkedList/DoublyLinkedList'1.ReverseEnumeration.cs @@ -9,121 +9,103 @@ namespace NetFabric { public partial class DoublyLinkedList { - public readonly struct ReverseEnumeration : IValueReadOnlyCollection + public readonly struct ReverseEnumeration + : IValueReadOnlyList { readonly DoublyLinkedList list; internal ReverseEnumeration(DoublyLinkedList list) - { - this.list = list; - } + => this.list = list; public int Count => list.count; + + public T this[int index] + { + get + { + if ((uint)index >= (uint)Count) + Throw.ArgumentOutOfRangeException(nameof(index)); + return index < Count / 2 + ? ReverseOffset(list.Last, index)!.Value + : ForwardOffset(list.First, Count - index - 1)!.Value; + } + } [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly Enumerator GetEnumerator() => - new Enumerator(list); + public Enumerator GetEnumerator() => + new(list); - readonly DisposableEnumerator IValueEnumerable.GetEnumerator() => - new DisposableEnumerator(list); + DisposableEnumerator IValueEnumerable.GetEnumerator() => + new(list); - readonly IEnumerator IEnumerable.GetEnumerator() => + IEnumerator IEnumerable.GetEnumerator() => new DisposableEnumerator(list); - readonly IEnumerator IEnumerable.GetEnumerator() => + IEnumerator IEnumerable.GetEnumerator() => new DisposableEnumerator(list); public struct Enumerator { readonly DoublyLinkedList list; readonly int version; - Node current; - EnumeratorState state; + Node? current; internal Enumerator(DoublyLinkedList list) { this.list = list; version = list.version; current = null; - state = list.IsEmpty ? EnumeratorState.Empty : EnumeratorState.First; } public readonly T Current - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => current.Value; - } + => current!.Value; public bool MoveNext() { - if (version != list.version) - ThrowInvalidOperation(); - - switch (state) - { - case EnumeratorState.Normal: - current = current.Previous; - return current is object; - case EnumeratorState.First: - current = list.tail; - state = EnumeratorState.Normal; - return true; - default: - return false; - } - - static void ThrowInvalidOperation() => throw new InvalidOperationException(); + if (version != list.version) Throw.InvalidOperationException(); + current = current is null + ? list.Last + : current.Previous; + return current is not null; } } - public struct DisposableEnumerator : IEnumerator + public struct DisposableEnumerator + : IEnumerator { readonly DoublyLinkedList list; readonly int version; - Node current; - EnumeratorState state; + Node? current; internal DisposableEnumerator(DoublyLinkedList list) { this.list = list; version = list.version; current = null; - state = list.IsEmpty ? EnumeratorState.Empty : EnumeratorState.First; } public readonly T Current - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => current.Value; - } - readonly object IEnumerator.Current => current.Value; + => current!.Value; + readonly object? IEnumerator.Current + => current!.Value; public bool MoveNext() { - if (version != list.version) - ThrowInvalidOperation(); - - switch (state) - { - case EnumeratorState.Normal: - current = current.Previous; - return current is object; - case EnumeratorState.First: - current = list.tail; - state = EnumeratorState.Normal; - return true; - default: - return false; - } - - static void ThrowInvalidOperation() => throw new InvalidOperationException(); + if (version != list.version) + Throw.InvalidOperationException(); + current = current is null + ? list.Last + : current.Previous; + return current is not null; } - public readonly void Reset() => throw new NotSupportedException(); + public readonly void Reset() + => Throw.NotSupportedException(); - public readonly void Dispose() { } + public readonly void Dispose() + { } } } } diff --git a/NetFabric.DoublyLinkedList/DoublyLinkedList'1.cs b/NetFabric.DoublyLinkedList/DoublyLinkedList'1.cs index b93907d..8c344fe 100644 --- a/NetFabric.DoublyLinkedList/DoublyLinkedList'1.cs +++ b/NetFabric.DoublyLinkedList/DoublyLinkedList'1.cs @@ -1,15 +1,16 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; namespace NetFabric { public partial class DoublyLinkedList { - public static readonly DoublyLinkedList Empty = new DoublyLinkedList(); + public static readonly DoublyLinkedList Empty = new(); - internal Node head; - internal Node tail; + internal Node? head; + internal Node? tail; int count; int version; @@ -33,10 +34,10 @@ public DoublyLinkedList(IReadOnlyList collection, bool reversed = false) : AddLast(collection, reversed); } - public Node First => + public Node? First => head; - public Node Last => + public Node? Last => tail; public int Count => @@ -50,11 +51,8 @@ public DoublyLinkedList(IReadOnlyList collection, bool reversed = false) : public Node AddAfter(Node node, T value) { - if (node is null) - ThrowNodeNull(); - - if (node.List != this) - ThrowInvalidOperation(); + if (node is null) Throw.ArgumentNullException(nameof(node)); + if (node.List != this) Throw.InvalidOperationException(); var result = new Node { @@ -71,18 +69,12 @@ public Node AddAfter(Node node, T value) count++; version++; return result; - - void ThrowNodeNull() => throw new ArgumentNullException(nameof(node)); - static void ThrowInvalidOperation() => throw new InvalidOperationException(); } public Node AddBefore(Node node, T value) { - if (node is null) - ThrowNodeNull(); - - if (node.List != this) - ThrowInvalidOperation(); + if (node is null) Throw.ArgumentNullException(nameof(node)); + if (node.List != this) Throw.InvalidOperationException(); var result = new Node { @@ -99,9 +91,6 @@ public Node AddBefore(Node node, T value) count++; version++; return result; - - void ThrowNodeNull() => throw new ArgumentNullException(nameof(node)); - static void ThrowInvalidOperation() => throw new InvalidOperationException(); } public Node AddFirst(T value) @@ -125,11 +114,10 @@ public Node AddFirst(T value) public void AddFirst(IEnumerable collection) { - if (collection is null) - ThrowCollectionNull(); + if (collection is null) Throw.ArgumentNullException(nameof(collection)); - Node tempHead = null; - Node tempTail = null; + Node? tempHead = null; + Node? tempTail = null; using (var enumerator = collection.GetEnumerator()) { if (enumerator.MoveNext()) @@ -173,14 +161,11 @@ public void AddFirst(IEnumerable collection) head = tempHead; } version++; - - void ThrowCollectionNull() => throw new ArgumentNullException(nameof(collection)); } public void AddFirst(IReadOnlyList collection, bool reversed = false) { - if (collection is null) - ThrowCollectionNull(); + if (collection is null) Throw.ArgumentNullException(nameof(collection)); if (collection.Count == 0) return; @@ -213,8 +198,6 @@ public void AddFirst(IReadOnlyList collection, bool reversed = false) count += collection.Count; version++; - void ThrowCollectionNull() => throw new ArgumentNullException(nameof(collection)); - void Assign() { for (int index = 1, end = collection.Count; index < end; index++) @@ -250,8 +233,7 @@ void AssignReversed() public void AddFirst(DoublyLinkedList list, bool reversed = false) { - if (list is null) - ThrowListNull(); + if (list is null) Throw.ArgumentNullException(nameof(list)); if (list.Count == 0) return; @@ -286,12 +268,10 @@ public void AddFirst(DoublyLinkedList list, bool reversed = false) count += list.count; version++; - void ThrowListNull() => throw new ArgumentNullException(nameof(list)); - void Assign() { current = current.Next; - while (current is object) + while (current is not null) { var node = new Node { @@ -310,7 +290,7 @@ void Assign() void AssignReversed() { current = current.Next; - while (current is object) + while (current is not null) { var node = new Node { @@ -329,8 +309,7 @@ void AssignReversed() public void AddFirstFrom(DoublyLinkedList list, bool reversed = false) { - if (list is null) - ThrowListNull(); + if (list is null) Throw.ArgumentNullException(nameof(list)); if (list.Count == 0) return; @@ -357,12 +336,10 @@ public void AddFirstFrom(DoublyLinkedList list, bool reversed = false) version++; list.Invalidate(); - void ThrowListNull() => throw new ArgumentNullException(nameof(list)); - void Assign() { var current = list.head; - while (current is object) + while (current is not null) { current.List = this; @@ -383,7 +360,7 @@ void AssignReversed() tempHead = tempTail = current; current = next; - while (current is object) + while (current is not null) { next = current.Next; @@ -419,11 +396,10 @@ public Node AddLast(T value) public void AddLast(IEnumerable collection) { - if (collection is null) - ThrowCollectionNull(); + if (collection is null) Throw.ArgumentNullException(nameof(collection)); - Node tempHead = null; - Node tempTail = null; + Node? tempHead = null; + Node? tempTail = null; using (var enumerator = collection.GetEnumerator()) { if (enumerator.MoveNext()) @@ -467,14 +443,11 @@ public void AddLast(IEnumerable collection) tail = tempTail; } version++; - - void ThrowCollectionNull() => throw new ArgumentNullException(nameof(collection)); } public void AddLast(IReadOnlyList collection, bool reversed = false) { - if (collection is null) - ThrowCollectionNull(); + if (collection is null) Throw.ArgumentNullException(nameof(collection)); if (collection.Count == 0) return; @@ -507,8 +480,6 @@ public void AddLast(IReadOnlyList collection, bool reversed = false) count += collection.Count; version++; - void ThrowCollectionNull() => throw new ArgumentNullException(nameof(collection)); - void Assign() { for (int index = 1, end = collection.Count; index < end; index++) @@ -544,8 +515,7 @@ void AssignReversed() public void AddLast(DoublyLinkedList list, bool reversed = false) { - if (list is null) - ThrowListNull(); + if (list is null) Throw.ArgumentNullException(nameof(list)); if (list.Count == 0) return; @@ -580,12 +550,10 @@ public void AddLast(DoublyLinkedList list, bool reversed = false) count += list.count; version++; - void ThrowListNull() => throw new ArgumentNullException(nameof(list)); - void Assign() { current = current.Next; - while (current is object) + while (current is not null) { var node = new Node { @@ -604,7 +572,7 @@ void Assign() void AssignReversed() { current = current.Next; - while (current is object) + while (current is not null) { var node = new Node { @@ -623,8 +591,7 @@ void AssignReversed() public void AddLastFrom(DoublyLinkedList list, bool reversed = false) { - if (list is null) - ThrowListNull(); + if (list is null) Throw.ArgumentNullException(nameof(list)); if (list.Count == 0) return; @@ -651,12 +618,10 @@ public void AddLastFrom(DoublyLinkedList list, bool reversed = false) version++; list.Invalidate(); - void ThrowListNull() => throw new ArgumentNullException(nameof(list)); - void Assign() { var current = list.head; - while (current is object) + while (current is not null) { current.List = this; @@ -677,7 +642,7 @@ void AssignReversed() tempHead = tempTail = current; current = next; - while (current is object) + while (current is not null) { next = current.Next; @@ -695,7 +660,7 @@ void AssignReversed() public void Clear() { var current = head; - while (current is object) + while (current is not null) { var temp = current; current = current.Next; @@ -713,12 +678,12 @@ internal void Invalidate() } [Pure] - public Node Find(T value) + public Node? Find(T value) { var node = head; if (value == null) { - while (node is object) + while (node is not null) { if (node.Value == null) return node; @@ -729,7 +694,7 @@ public Node Find(T value) else { var comparer = EqualityComparer.Default; - while (node is object) + while (node is not null) { if (comparer.Equals(node.Value, value)) return node; @@ -741,12 +706,12 @@ public Node Find(T value) } [Pure] - public Node FindLast(T value) + public Node? FindLast(T value) { var node = tail; if (value == null) { - while (node is object) + while (node is not null) { if (node.Value == null) return node; @@ -757,7 +722,7 @@ public Node FindLast(T value) else { var comparer = EqualityComparer.Default; - while (node is object) + while (node is not null) { if (comparer.Equals(node.Value, value)) return node; @@ -769,12 +734,12 @@ public Node FindLast(T value) } [Pure] - public ForwardEnumeration EnumerateForward() => - new ForwardEnumeration(this); + public ForwardEnumeration Forward => + new(this); [Pure] - public ReverseEnumeration EnumerateReversed() => - new ReverseEnumeration(this); + public ReverseEnumeration Backward => + new(this); public bool Remove(T value) { @@ -856,8 +821,7 @@ public bool RemoveLast(T value) public void RemoveFirst() { - if (IsEmpty) - ThrowInvalidOperation(); + if (IsEmpty) Throw.InvalidOperationException(); var node = head; if (tail == node) @@ -873,14 +837,11 @@ public void RemoveFirst() node.Invalidate(); count--; version++; - - static void ThrowInvalidOperation() => throw new InvalidOperationException(); } public void RemoveLast() { - if (IsEmpty) - ThrowInvalidOperation(); + if (IsEmpty) Throw.InvalidOperationException(); var node = tail; if (head == node) @@ -896,8 +857,6 @@ public void RemoveLast() node.Invalidate(); count--; version++; - - static void ThrowInvalidOperation() => throw new InvalidOperationException(); } [Pure] @@ -912,7 +871,7 @@ public DoublyLinkedList Clone() }; var current = head; - if (current is object) + if (current is not null) { list.head = list.tail = new Node { @@ -922,7 +881,7 @@ public DoublyLinkedList Clone() Previous = null, }; current = current.Next; - while (current is object) + while (current is not null) { var node = new Node { @@ -952,7 +911,7 @@ public DoublyLinkedList Reverse() }; var current = head; - if (current is object) + if (current is not null) { list.head = list.tail = new Node { @@ -962,7 +921,7 @@ public DoublyLinkedList Reverse() Previous = null, }; current = current.Next; - while (current is object) + while (current is not null) { var node = new Node { @@ -987,7 +946,7 @@ public void ReverseInPlace() Node temp; var current = head; - while (current is object) + while (current is not null) { temp = current.Next; current.Next = current.Previous; @@ -999,5 +958,22 @@ public void ReverseInPlace() tail = temp; version++; } + + static Node? ForwardOffset(Node? node, int offset) + { + for (var counter = 0; counter < offset; counter++) + node = node!.Next; + + return node; + } + + static Node? ReverseOffset(Node? node, int offset) + { + for (var counter = 0; counter < offset; counter++) + node = node!.Previous; + + return node; + } + } } diff --git a/NetFabric.DoublyLinkedList/DoublyLinkedList.cs b/NetFabric.DoublyLinkedList/DoublyLinkedList.cs index 4530c59..f32c74d 100644 --- a/NetFabric.DoublyLinkedList/DoublyLinkedList.cs +++ b/NetFabric.DoublyLinkedList/DoublyLinkedList.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("NetFabric.DoublyLinkedList.Tests")] @@ -10,19 +11,13 @@ public static class DoublyLinkedList [Pure] public static DoublyLinkedList Append(DoublyLinkedList left, DoublyLinkedList right) { - if (left is null) - ThrowLeftNull(); - - if (right is null) - ThrowRightNull(); + if (left is null) Throw.ArgumentNullException(nameof(left)); + if (right is null) Throw.ArgumentNullException(nameof(right)); var result = new DoublyLinkedList(); result.AddLast(left); result.AddLast(right); return result; - - void ThrowLeftNull() => throw new ArgumentNullException(nameof(left)); - void ThrowRightNull() => throw new ArgumentNullException(nameof(right)); } } } diff --git a/NetFabric.DoublyLinkedList/EnumeratorState.cs b/NetFabric.DoublyLinkedList/EnumeratorState.cs deleted file mode 100644 index e9ab6d5..0000000 --- a/NetFabric.DoublyLinkedList/EnumeratorState.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace NetFabric -{ - enum EnumeratorState - { - Normal, - First, - Empty, - } -} diff --git a/NetFabric.DoublyLinkedList/NetFabric.DoublyLinkedList.csproj b/NetFabric.DoublyLinkedList/NetFabric.DoublyLinkedList.csproj index 4178a90..e07db33 100644 --- a/NetFabric.DoublyLinkedList/NetFabric.DoublyLinkedList.csproj +++ b/NetFabric.DoublyLinkedList/NetFabric.DoublyLinkedList.csproj @@ -5,16 +5,18 @@ NetFabric.DoublyLinkedList NetFabric.DoublyLinkedList An alternative to System.Collection.Generics.LinkedList with reverse operation and enumeration without allocation. - 2.2.0 + 3.0.0 Antao Almada - Copyright 2018-2019 Antao Almada + Copyright 2018-2021 Antao Almada Icon.png LICENSE collections, data structures, algorithms, list NetFabric - Set methods as pure. + true CONTRACTS_FULL + true + snupkg @@ -27,11 +29,15 @@ - + + all + runtime; build; native; contentfiles; analyzers + all runtime; build; native; contentfiles; analyzers; buildtransitive + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/NetFabric.DoublyLinkedList/Throw.cs b/NetFabric.DoublyLinkedList/Throw.cs new file mode 100644 index 0000000..3fc18eb --- /dev/null +++ b/NetFabric.DoublyLinkedList/Throw.cs @@ -0,0 +1,39 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + +namespace NetFabric +{ + static class Throw + { + [DoesNotReturn] + [MethodImpl(MethodImplOptions.NoInlining)] + public static void ArgumentException(string paramName, string? message = default) + => throw new ArgumentException(paramName: paramName, message: message); + + [DoesNotReturn] + [MethodImpl(MethodImplOptions.NoInlining)] + public static void ArgumentNullException(string paramName, string? message = default) + => throw new ArgumentNullException(paramName: paramName, message: message); + + [DoesNotReturn] + [MethodImpl(MethodImplOptions.NoInlining)] + public static T ArgumentNullException(string paramName, string? message = default) + => throw new ArgumentNullException(paramName: paramName, message: message); + + [DoesNotReturn] + [MethodImpl(MethodImplOptions.NoInlining)] + public static void InvalidOperationException(string? message = default) + => throw new InvalidOperationException(message); + + [DoesNotReturn] + [MethodImpl(MethodImplOptions.NoInlining)] + public static void NotSupportedException() + => throw new NotSupportedException(); + + [DoesNotReturn] + [MethodImpl(MethodImplOptions.NoInlining)] + public static void ArgumentOutOfRangeException(string paramName, string? message = default) + => throw new ArgumentOutOfRangeException(paramName: paramName, message: message); + } +} \ No newline at end of file diff --git a/README.md b/README.md index bd737fd..424e1a7 100644 --- a/README.md +++ b/README.md @@ -14,23 +14,54 @@ New overrides and methods were added to minimizing the memory allocations, numbe ## Benchmarks -Performance comparison between [`System.Collections.Generic.LinkedList`](https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.linkedlist-1) and `DoublyLinkedList`. Shorter is better. +Performance comparison between [`System.Collections.Generic.LinkedList`](https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.linkedlist-1) and `DoublyLinkedList`. -![constructor performance](https://user-images.githubusercontent.com/534533/49696258-7a98b480-fb9f-11e8-9a06-3585e4dc684b.png) - -![forward enumeration performance](https://user-images.githubusercontent.com/534533/49405413-40b45200-f74a-11e8-9b63-4ac67efd144d.png) +The benchmarks project is part of the repository. You can check the code and run it on your machine. -![reverse enumeration performance](https://user-images.githubusercontent.com/534533/49405544-a43e7f80-f74a-11e8-9aba-544cb3141e33.png) +### Constructor performance + +| Method | Count | Mean | Error | StdDev | Median | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | +|------------------------------- |------ |-------------:|-----------:|-----------:|-------------:|------:|--------:|-------:|------:|------:|----------:| +| **LinkedList_Enumerable** | **0** | **12.082 ns** | **0.3174 ns** | **0.4942 ns** | **12.091 ns** | **1.00** | **0.00** | **0.0191** | **-** | **-** | **40 B** | +| LinkedList_List | 0 | 21.542 ns | 0.9611 ns | 2.7884 ns | 19.863 ns | 2.07 | 0.21 | 0.0382 | - | - | 80 B | +| DoublyLinkedList_Enumerable | 0 | 10.749 ns | 0.0741 ns | 0.0657 ns | 10.761 ns | 0.89 | 0.04 | 0.0191 | - | - | 40 B | +| DoublyLinkedList_List | 0 | 7.533 ns | 0.1320 ns | 0.1170 ns | 7.511 ns | 0.62 | 0.03 | 0.0191 | - | - | 40 B | +| DoublyLinkedList_List_Reversed | 0 | 7.391 ns | 0.1275 ns | 0.1192 ns | 7.386 ns | 0.61 | 0.03 | 0.0191 | - | - | 40 B | +| | | | | | | | | | | | | +| **LinkedList_Enumerable** | **10** | **205.166 ns** | **1.2539 ns** | **1.1116 ns** | **205.147 ns** | **1.00** | **0.00** | **0.2677** | **-** | **-** | **560 B** | +| LinkedList_List | 10 | 241.563 ns | 1.7717 ns | 2.3651 ns | 241.213 ns | 1.18 | 0.01 | 0.2675 | - | - | 560 B | +| DoublyLinkedList_Enumerable | 10 | 163.873 ns | 1.8013 ns | 1.5042 ns | 163.424 ns | 0.80 | 0.01 | 0.2677 | - | - | 560 B | +| DoublyLinkedList_List | 10 | 142.060 ns | 1.3490 ns | 1.1265 ns | 142.538 ns | 0.69 | 0.01 | 0.2487 | - | - | 520 B | +| DoublyLinkedList_List_Reversed | 10 | 139.378 ns | 1.0579 ns | 0.8834 ns | 139.184 ns | 0.68 | 0.00 | 0.2487 | - | - | 520 B | +| | | | | | | | | | | | | +| **LinkedList_Enumerable** | **100** | **1,804.348 ns** | **16.3064 ns** | **15.2530 ns** | **1,808.247 ns** | **1.00** | **0.00** | **2.3327** | **-** | **-** | **4,880 B** | +| LinkedList_List | 100 | 1,894.060 ns | 12.0334 ns | 11.2561 ns | 1,895.876 ns | 1.05 | 0.01 | 2.3327 | - | - | 4,880 B | +| DoublyLinkedList_Enumerable | 100 | 1,355.031 ns | 16.9081 ns | 14.1191 ns | 1,352.691 ns | 0.75 | 0.01 | 2.3327 | - | - | 4,880 B | +| DoublyLinkedList_List | 100 | 1,216.764 ns | 11.3984 ns | 10.6620 ns | 1,218.606 ns | 0.67 | 0.01 | 2.3136 | - | - | 4,840 B | +| DoublyLinkedList_List_Reversed | 100 | 1,233.906 ns | 11.7235 ns | 10.9662 ns | 1,238.096 ns | 0.68 | 0.01 | 2.3136 | - | - | 4,840 B | + +### Enumeration performance + +| Method | Categories | Count | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | +|--------------------------------- |----------- |------- |---------:|--------:|--------:|------:|--------:|--------:|--------:|--------:|----------:| +| LinkedList_Forward_While | Forward | 100000 | 320.0 μs | 3.77 μs | 3.53 μs | 1.00 | 0.00 | - | - | - | - | +| LinkedList_Forward_ForEach | Forward | 100000 | 496.1 μs | 5.55 μs | 5.19 μs | 1.55 | 0.02 | - | - | - | - | +| DoublyLinkedList_Forward_While | Forward | 100000 | 306.3 μs | 3.37 μs | 3.15 μs | 0.96 | 0.01 | - | - | - | - | +| DoublyLinkedList_Forward_ForEach | Forward | 100000 | 470.0 μs | 4.44 μs | 3.70 μs | 1.47 | 0.01 | - | - | - | - | +| | | | | | | | | | | | | +| LinkedList_Reverse_While | Reverse | 100000 | 353.5 μs | 6.03 μs | 5.03 μs | 1.00 | 0.00 | - | - | - | - | +| LinkedList_Reverse_ForEach | Reverse | 100000 | 934.6 μs | 7.46 μs | 6.23 μs | 2.64 | 0.04 | 41.0156 | 41.0156 | 41.0156 | 400,393 B | +| DoublyLinkedList_Reverse_While | Reverse | 100000 | 356.4 μs | 2.89 μs | 2.70 μs | 1.01 | 0.02 | - | - | - | - | +| DoublyLinkedList_Reverse_ForEach | Reverse | 100000 | 499.3 μs | 6.13 μs | 5.44 μs | 1.42 | 0.03 | - | - | - | - | -The benchmarks project is part of the repository. You can check the code and run it on your machine. ## Forward and reverse enumeration -`DoublyLinkedList` does not implement `IEnumerable`. Call the methods `EnumerateForward()` or `EnumerateReversed()` to get an enumerator that goes in the direction you require. +`DoublyLinkedList` does not directly implement `IEnumerable`. Call the properties `Forward` or `Backward` to get an enumerator that goes in the direction you require. ```csharp var list = new DoublyLinkedList(new[] {1, 2, 3, 4}); -foreach (var item in list.EnumerateReversed()) +foreach (var item in list.Backward) Console.Write(item); ``` outputs @@ -42,7 +73,7 @@ These methods allow the use of LINQ on either direction: ```csharp var list = new DoublyLinkedList(new[] {1, 2, 3, 4}); -Console.Write(list.EnumerateReversed().First()); +Console.Write(list.Backward.First()); ``` outputs ``` @@ -60,6 +91,15 @@ while (current is object) } ``` +The enumerators also supports random access by using an indexer. It allows indexing relative to the head or the tail of the list. + +```csharp +var head = doublyLinkedList.Forward[0]; +var tail = doublyLinkedList.Backward[0]; +``` + +Please note that this `DoublyLinkedList` is not optimized for random access. It will use the shortest path to the item but it will have to step through the list to get there. + ## `AddFirst()` and `AddLast()` These methods now support the addition of collections of items. These can come from an `IEnumerable`, `IReadOnlyList` or another `DoublyLinkedList`. @@ -70,7 +110,7 @@ When the parameter is `IReadOnlyList` or `DoublyLinkedList`, they can be a var list = new DoublyLinkedList(new[] {1, 2, 3, 4}); var anotherList = new DoublyLinkedList(new[] {5, 6, 7, 8}); list.AddFirst(anotherList, true); -foreach (var item in list.EnumerateForward()) +foreach (var item in list.Forward) Console.Write(item); ``` @@ -94,7 +134,7 @@ A static method that returns a `DoublyLinkedList` instance with the items of var list = DoublyLinkedList.Append( new DoublyLinkedList(new[] {1, 2, 3, 4}), new DoublyLinkedList(new[] {5, 6, 7, 8})); -foreach (var item in list.EnumerateForward()) +foreach (var item in list.Forward) Console.Write(item); ``` @@ -111,7 +151,7 @@ Returns a shallow clone of the a `DoublyLinkedList`. ```csharp var list = new DoublyLinkedList(new[] {1, 2, 3, 4}); var clone = list.Clone(); -foreach (var item in list.EnumerateForward()) +foreach (var item in list.Forward) Console.Write(item); ``` @@ -128,7 +168,7 @@ Returns a shallow clone of the a `DoublyLinkedList` with the items in reverse ```csharp var list = new DoublyLinkedList(new[] {1, 2, 3, 4}); var reversed = list.Reverse(); -foreach (var item in reversed.EnumerateForward()) +foreach (var item in reversed.Forward) Console.Write(item); ``` @@ -145,7 +185,7 @@ outputs ```csharp var list = new DoublyLinkedList(new[] {1, 2, 3, 4}); list.ReverseInPlace(); -foreach (var item in list.EnumerateForward()) +foreach (var item in list.Forward) Console.Write(item); ```