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  }