From ad86747112570372f0c58e18376510eae7813640 Mon Sep 17 00:00:00 2001 From: fm4teus Date: Wed, 1 Oct 2025 14:17:44 -0300 Subject: [PATCH 1/2] contains --- linkedhashmap.go | 8 +++--- linkedhashmap_test.go | 33 +++++++++++++++++----- linkedhashset.go | 6 ++++ linkedhashset_benchmark_test.go | 49 +++++++++++++++++++++++++++++++++ linkedhashset_test.go | 29 +++++++++++++++++++ 5 files changed, 114 insertions(+), 11 deletions(-) create mode 100644 linkedhashset_benchmark_test.go diff --git a/linkedhashmap.go b/linkedhashmap.go index 85b237d..04e994d 100644 --- a/linkedhashmap.go +++ b/linkedhashmap.go @@ -61,21 +61,21 @@ func (l *linkedHashMap) Put(key, value interface{}) { } // Get gets an entry from the linked hash map -func (l *linkedHashMap) Get(key interface{}) interface{} { +func (l *linkedHashMap) Get(key interface{}) (value interface{}, found bool) { hash := l.hash(key) if _, ok := l.table[hash]; !ok { - return nil + return nil, false } tmp := l.table[hash] for tmp != nil { if tmp.key == key { - return tmp.value + return tmp.value, true } tmp = tmp.after } - return nil + return nil, false } // Remove removes an entry from the linked hash map diff --git a/linkedhashmap_test.go b/linkedhashmap_test.go index ef0a45f..391d1e7 100644 --- a/linkedhashmap_test.go +++ b/linkedhashmap_test.go @@ -12,13 +12,17 @@ func TestGet(t *testing.T) { set := newLinkedHashMap() value := rand.Int() set.Put("test", value) - require.Equal(t, value, set.Get("test")) + + v, exists := set.Get("test") + require.Equal(t, value, v) + require.True(t, exists) }) t.Run("When the key not exists", func(t *testing.T) { set := newLinkedHashMap() - result := set.Get("bla") + result, exists := set.Get("bla") require.Nil(t, result) + require.False(t, exists) }) } @@ -27,7 +31,10 @@ func TestPut(t *testing.T) { set := newLinkedHashMap() value := rand.Int() set.Put("test", value) - require.Equal(t, value, set.Get("test")) + + v, exists := set.Get("test") + require.Equal(t, value, v) + require.True(t, exists) }) t.Run("invalid key", func(t *testing.T) { @@ -50,7 +57,10 @@ func TestRemove(t *testing.T) { set.Remove(1) require.Equal(t, 2, set.Length()) - require.Nil(t, set.Get(1)) + + v, exists := set.Get(1) + require.Nil(t, v) + require.False(t, exists) }) t.Run("last value", func(t *testing.T) { @@ -61,7 +71,10 @@ func TestRemove(t *testing.T) { set.Remove(3) require.Equal(t, 2, set.Length()) - require.Nil(t, set.Get(3)) + + v, exists := set.Get(3) + require.Nil(t, v) + require.False(t, exists) }) t.Run("middle value", func(t *testing.T) { @@ -72,7 +85,10 @@ func TestRemove(t *testing.T) { set.Remove(2) require.Equal(t, 2, set.Length()) - require.Nil(t, set.Get(2)) + + v, exists := set.Get(2) + require.Nil(t, v) + require.False(t, exists) }) t.Run("single value", func(t *testing.T) { @@ -80,6 +96,9 @@ func TestRemove(t *testing.T) { set.Put(1, 1) set.Remove(1) require.Equal(t, 0, set.Length()) - require.Nil(t, set.Get(1)) + + v, exists := set.Get(1) + require.Nil(t, v) + require.False(t, exists) }) } diff --git a/linkedhashset.go b/linkedhashset.go index df2a1a2..0e9e58b 100644 --- a/linkedhashset.go +++ b/linkedhashset.go @@ -61,6 +61,7 @@ func (l *LinkedHashSet[T]) AsInterface() []interface{} { } // InArray returns whether the given item is in array or not +// DEPRECATED: use Contains method instead func (l *LinkedHashSet[T]) InArray(search T) bool { for item := range l.Iter() { if item == search { @@ -80,3 +81,8 @@ func NewLinkedHashSet[T comparable](items ...T) *LinkedHashSet[T] { } return lhm } + +func (l *LinkedHashSet[T]) Contains(search T) bool { + _, contains := l.linkedHashMap.Get(search) + return contains +} diff --git a/linkedhashset_benchmark_test.go b/linkedhashset_benchmark_test.go new file mode 100644 index 0000000..8f93f76 --- /dev/null +++ b/linkedhashset_benchmark_test.go @@ -0,0 +1,49 @@ +package set + +import "testing" + +func BenchmarkLinkedHashSet_Contains_vs_InArray(b *testing.B) { + set := NewLinkedHashSet[string]() + set.Add(giantGenericSlice...) + + foundTarget := giantGenericSlice[len(giantGenericSlice)/2] + notFoundTarget := "___not_present___" + + b.Run("Found", func(b *testing.B) { + b.Run("Contains", func(b *testing.B) { + b.ReportAllocs() + var sink bool + for i := 0; i < b.N; i++ { + sink = set.Contains(foundTarget) + } + _ = sink + }) + b.Run("InArray", func(b *testing.B) { + b.ReportAllocs() + var sink bool + for i := 0; i < b.N; i++ { + sink = set.InArray(foundTarget) + } + _ = sink + }) + }) + + b.Run("NotFound", func(b *testing.B) { + b.Run("Contains", func(b *testing.B) { + b.ReportAllocs() + var sink bool + for i := 0; i < b.N; i++ { + sink = set.Contains(notFoundTarget) + } + _ = sink + }) + b.Run("InArray", func(b *testing.B) { + b.ReportAllocs() + var sink bool + for i := 0; i < b.N; i++ { + sink = set.InArray(notFoundTarget) + } + _ = sink + }) + }) +} diff --git a/linkedhashset_test.go b/linkedhashset_test.go index ce19394..e5c359e 100644 --- a/linkedhashset_test.go +++ b/linkedhashset_test.go @@ -196,3 +196,32 @@ func TestLinkedHashSetAsInterface(t *testing.T) { require.Equal(t, value.(testStruct), expectedArray[i]) } } + +func TestLinkedHashSetContains(t *testing.T) { + t.Run("found", func(t *testing.T) { + set := NewLinkedHashSet("02", "04", "06", "08") + println(set.linkedHashMap.table) + println(set.linkedHashMap.Get("02")) // debug + + require.True(t, set.Contains("02")) + require.True(t, set.Contains("04")) + require.True(t, set.Contains("06")) + require.True(t, set.Contains("08")) + }) + + t.Run("not found", func(t *testing.T) { + set := NewLinkedHashSet("02", "04", "06", "08") + require.False(t, set.Contains("01")) + require.False(t, set.Contains("03")) + require.False(t, set.Contains("05")) + require.False(t, set.Contains("07")) + }) + + t.Run("empty", func(t *testing.T) { + set := NewLinkedHashSet[string]() + require.False(t, set.Contains("01")) + require.False(t, set.Contains("03")) + require.False(t, set.Contains("05")) + require.False(t, set.Contains("07")) + }) +} From f7bc4887d4ef451a9263418c5eea88c7537ab28c Mon Sep 17 00:00:00 2001 From: fm4teus Date: Wed, 1 Oct 2025 14:40:43 -0300 Subject: [PATCH 2/2] rm debug --- linkedhashset_benchmark_test.go | 86 ++++++++++++++++++--------------- linkedhashset_test.go | 2 - 2 files changed, 46 insertions(+), 42 deletions(-) diff --git a/linkedhashset_benchmark_test.go b/linkedhashset_benchmark_test.go index 8f93f76..dcbac53 100644 --- a/linkedhashset_benchmark_test.go +++ b/linkedhashset_benchmark_test.go @@ -1,49 +1,55 @@ package set -import "testing" +import ( + "fmt" + "testing" +) func BenchmarkLinkedHashSet_Contains_vs_InArray(b *testing.B) { - set := NewLinkedHashSet[string]() - set.Add(giantGenericSlice...) + total := len(giantGenericSlice) + step := total / 5 + sizes := []int{step, 2 * step, 3 * step, 4 * step, total} - foundTarget := giantGenericSlice[len(giantGenericSlice)/2] - notFoundTarget := "___not_present___" + for _, n := range sizes { + b.Run(fmt.Sprintf("N=%d", n), func(b *testing.B) { + set := NewLinkedHashSet[string]() + set.Add(giantGenericSlice[:n]...) - b.Run("Found", func(b *testing.B) { - b.Run("Contains", func(b *testing.B) { - b.ReportAllocs() - var sink bool - for i := 0; i < b.N; i++ { - sink = set.Contains(foundTarget) - } - _ = sink - }) - b.Run("InArray", func(b *testing.B) { - b.ReportAllocs() - var sink bool - for i := 0; i < b.N; i++ { - sink = set.InArray(foundTarget) - } - _ = sink - }) - }) + foundTarget := giantGenericSlice[n/2] + notFoundTarget := "___not_present___" - b.Run("NotFound", func(b *testing.B) { - b.Run("Contains", func(b *testing.B) { - b.ReportAllocs() - var sink bool - for i := 0; i < b.N; i++ { - sink = set.Contains(notFoundTarget) - } - _ = sink - }) - b.Run("InArray", func(b *testing.B) { - b.ReportAllocs() - var sink bool - for i := 0; i < b.N; i++ { - sink = set.InArray(notFoundTarget) - } - _ = sink + b.Run("Found/Contains", func(b *testing.B) { + b.ReportAllocs() + var sink bool + for i := 0; i < b.N; i++ { + sink = set.Contains(foundTarget) + } + _ = sink + }) + b.Run("Found/InArray", func(b *testing.B) { + b.ReportAllocs() + var sink bool + for i := 0; i < b.N; i++ { + sink = set.InArray(foundTarget) + } + _ = sink + }) + b.Run("NotFound/Contains", func(b *testing.B) { + b.ReportAllocs() + var sink bool + for i := 0; i < b.N; i++ { + sink = set.Contains(notFoundTarget) + } + _ = sink + }) + b.Run("NotFound/InArray", func(b *testing.B) { + b.ReportAllocs() + var sink bool + for i := 0; i < b.N; i++ { + sink = set.InArray(notFoundTarget) + } + _ = sink + }) }) - }) + } } diff --git a/linkedhashset_test.go b/linkedhashset_test.go index e5c359e..46b8e3c 100644 --- a/linkedhashset_test.go +++ b/linkedhashset_test.go @@ -200,8 +200,6 @@ func TestLinkedHashSetAsInterface(t *testing.T) { func TestLinkedHashSetContains(t *testing.T) { t.Run("found", func(t *testing.T) { set := NewLinkedHashSet("02", "04", "06", "08") - println(set.linkedHashMap.table) - println(set.linkedHashMap.Get("02")) // debug require.True(t, set.Contains("02")) require.True(t, set.Contains("04"))