github.com/searKing/golang/go@v1.2.74/util/spliterator/distinct_spliterator.go (about) 1 // Copyright 2020 The searKing Author. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package spliterator 6 7 import ( 8 "context" 9 "sync" 10 11 "github.com/searKing/golang/go/util/function/consumer" 12 "github.com/searKing/golang/go/util/object" 13 "github.com/searKing/golang/go/util/optional" 14 ) 15 16 //go:generate go-syncmap -type "seenMap<interface{}, struct{}>" 17 type seenMap sync.Map 18 19 /** 20 * A wrapping spliterator that only reports distinct elements of the 21 * underlying spliterator. Does not preserve size and encounter order. 22 */ 23 type distinctSpliterator struct { 24 consumer.TODO 25 // The underlying spliterator 26 s Spliterator 27 // ConcurrentHashMap holding distinct elements as keys 28 seen *seenMap 29 // Temporary element, only used with tryAdvance 30 tmpSlot optional.Optional 31 } 32 33 func NewDistinctSpliterator(s Spliterator) Spliterator { 34 return NewDistinctSpliterator2(s, &seenMap{}) 35 } 36 37 func NewDistinctSpliterator2(s Spliterator, seen *seenMap) Spliterator { 38 split := &distinctSpliterator{ 39 s: s, 40 seen: seen, 41 } 42 split.SetDerived(split) 43 return split 44 } 45 46 func (split *distinctSpliterator) Accept(t interface{}) { 47 split.tmpSlot = optional.Of(t) 48 } 49 50 func (split *distinctSpliterator) TrySplit() Spliterator { 51 if ls := split.s.TrySplit(); ls != nil { 52 return NewDistinctSpliterator2(ls, split.seen) 53 } 54 return nil 55 } 56 57 func (split *distinctSpliterator) TryAdvance(ctx context.Context, action consumer.Consumer) bool { 58 for { 59 if !split.s.TryAdvance(ctx, split) { 60 break 61 } 62 if !split.tmpSlot.IsPresent() { 63 break 64 } 65 if _, loaded := split.seen.LoadOrStore(split.tmpSlot.Get(), struct{}{}); !loaded { 66 action.Accept(split.tmpSlot.Get()) 67 split.tmpSlot = optional.Empty() 68 return true 69 } 70 } 71 return false 72 } 73 74 func (split *distinctSpliterator) ForEachRemaining(ctx context.Context, action consumer.Consumer) { 75 split.s.ForEachRemaining(ctx, consumer.ConsumerFunc(func(t interface{}) { 76 if _, loaded := split.seen.LoadOrStore(t, struct{}{}); !loaded { 77 action.Accept(t) 78 } 79 })) 80 } 81 82 func (split *distinctSpliterator) EstimateSize() int { 83 return split.s.EstimateSize() 84 } 85 86 func (split *distinctSpliterator) GetExactSizeIfKnown() int { 87 return split.s.GetExactSizeIfKnown() 88 } 89 90 func (split *distinctSpliterator) Characteristics() Characteristic { 91 return (split.s.Characteristics() & (^(CharacteristicSized | 92 CharacteristicSubsized | 93 CharacteristicSorted | 94 CharacteristicOrdered))) | 95 CharacteristicDistinct 96 } 97 98 func (split *distinctSpliterator) HasCharacteristics(characteristics Characteristic) bool { 99 return split.s.HasCharacteristics(characteristics) 100 } 101 102 func (split *distinctSpliterator) GetComparator() object.Comparator { 103 return split.s.GetComparator() 104 }