diff --git a/cache.go b/cache.go index 3f2bd2d..d43dd9c 100644 --- a/cache.go +++ b/cache.go @@ -445,14 +445,45 @@ func (c *Cache[K, V]) Len() int { c.items.mu.RLock() defer c.items.mu.RUnlock() - size := 0 - for _, elem := range c.items.values { - if !elem.Value.(*Item[K, V]).isExpiredUnsafe() { - size++ + total := c.items.expQueue.Len() + if total == 0 { + return 0 + } + + // search the heap-based expQueue by BFS + countExpired := func() int { + var ( + q []int + res int + ) + + item := c.items.expQueue[0].Value.(*Item[K, V]) + if !item.isExpiredUnsafe() { + return res + } + + q = append(q, 0) + for len(q) > 0 { + pop := q[0] + q = q[1:] + res++ + + for i := 1; i <= 2; i++ { + idx := 2*pop + i + if idx >= total { + break + } + + item = c.items.expQueue[idx].Value.(*Item[K, V]) + if item.isExpiredUnsafe() { + q = append(q, idx) + } + } } + return res } - return size + return total - countExpired() } // Keys returns all unexpired keys in the cache. diff --git a/cache_test.go b/cache_test.go index ac7db42..95fa9e4 100644 --- a/cache_test.go +++ b/cache_test.go @@ -803,8 +803,19 @@ func Test_Cache_Touch(t *testing.T) { } func Test_Cache_Len(t *testing.T) { - cache := prepCache(time.Hour, "1", "2") - addToCache(cache, time.Nanosecond, "3") + cache := prepCache(time.Hour) + assert.Equal(t, 0, cache.Len()) + + addToCache(cache, time.Hour, "1") + assert.Equal(t, 1, cache.Len()) + + addToCache(cache, time.Nanosecond, "2") + assert.Equal(t, 1, cache.Len()) + + addToCache(cache, time.Hour, "3") + for i := 4; i < 30; i++ { + addToCache(cache, time.Nanosecond, fmt.Sprint(i)) + } assert.Equal(t, 2, cache.Len()) }