blob: 37aee4839027f834871ef34894a073398d14ca0f [file] [log] [blame]
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:collection';
import 'unmodifiable_wrappers.dart';
/// A single set that provides a view of the union over a set of sets.
///
/// Since this is just a view, it reflects all changes in the underlying sets.
///
/// If an element is in multiple sets and the outer set is ordered, the version
/// in the earliest inner set is preferred. Component sets are assumed to use
/// `==` and `hashCode` for equality.
class UnionSet<E> extends SetBase<E> with UnmodifiableSetMixin<E> {
/// The set of sets that this provides a view of.
final Set<Set<E>> _sets;
/// Whether the sets in [_sets] are guaranteed to be disjoint.
final bool _disjoint;
/// Creates a new set that's a view of the union of all sets in [sets].
///
/// If any sets in [sets] change, this [UnionSet] reflects that change. If a
/// new set is added to [sets], this [UnionSet] reflects that as well.
///
/// If [disjoint] is `true`, then all component sets must be disjoint. That
/// is, that they contain no elements in common. This makes many operations
/// including [length] more efficient. If the component sets turn out not to
/// be disjoint, some operations may behave inconsistently.
UnionSet(Set<Set<E>> sets, {bool disjoint = false})
: _sets = sets,
_disjoint = disjoint;
/// Creates a new set that's a view of the union of all sets in [sets].
///
/// If any sets in [sets] change, this [UnionSet] reflects that change.
/// However, unlike [new UnionSet], this creates a copy of its parameter, so
/// changes in [sets] aren't reflected in this [UnionSet].
///
/// If [disjoint] is `true`, then all component sets must be disjoint. That
/// is, that they contain no elements in common. This makes many operations
/// including [length] more efficient. If the component sets turn out not to
/// be disjoint, some operations may behave inconsistently.
UnionSet.from(Iterable<Set<E>> sets, {bool disjoint = false})
: this(sets.toSet(), disjoint: disjoint);
@override
int get length => _disjoint
? _sets.fold(0, (length, set) => length + set.length)
: _iterable.length;
@override
Iterator<E> get iterator => _iterable.iterator;
/// An iterable over the contents of all [_sets].
///
/// If this is not a [_disjoint] union an extra set is used to deduplicate
/// values.
Iterable<E> get _iterable {
var allElements = _sets.expand((set) => set);
return _disjoint ? allElements : allElements.where(<E>{}.add);
}
@override
bool contains(Object? element) => _sets.any((set) => set.contains(element));
@override
E? lookup(Object? element) {
for (var set in _sets) {
var result = set.lookup(element);
if (result != null || set.contains(null)) return result;
}
return null;
}
@override
Set<E> toSet() => <E>{for (var set in _sets) ...set};
}