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..dcbac53 --- /dev/null +++ b/linkedhashset_benchmark_test.go @@ -0,0 +1,55 @@ +package set + +import ( + "fmt" + "testing" +) + +func BenchmarkLinkedHashSet_Contains_vs_InArray(b *testing.B) { + total := len(giantGenericSlice) + step := total / 5 + sizes := []int{step, 2 * step, 3 * step, 4 * step, total} + + for _, n := range sizes { + b.Run(fmt.Sprintf("N=%d", n), func(b *testing.B) { + set := NewLinkedHashSet[string]() + set.Add(giantGenericSlice[:n]...) + + foundTarget := giantGenericSlice[n/2] + notFoundTarget := "___not_present___" + + 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 ce19394..46b8e3c 100644 --- a/linkedhashset_test.go +++ b/linkedhashset_test.go @@ -196,3 +196,30 @@ 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") + + 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")) + }) +}