Class: Weak::Set
- Inherits:
-
Object
- Object
- Weak::Set
- Includes:
- Enumerable, [ Weak[ Weak::Set[ Weak::Set::WeakKeysWithDelete, Weak::Set::WeakKeys, Weak::Set::StrongKeys, Weak::Set::StrongSecondaryKeys ].find(&:usable?)
- Defined in:
- lib/weak/set.rb,
lib/weak/set/weak_keys.rb,
lib/weak/set/strong_keys.rb,
lib/weak/set/strong_secondary_keys.rb,
lib/weak/set/weak_keys_with_delete.rb
Overview
This library provides the Weak::Set class. It behaves similar to the
::Set class of the Ruby standard library, but all values are only weakly
referenced. That way, all values can be garbage collected and silently
removed from the set unless they are still referenced from some other live
object.
Set uses ObjectSpace::WeakMap as storage, so you must note the
following points:
- Equality of elements is determined strictly by their object identity
instead of
Object#eql?orObject#hashas the Set does by default. - Elements can be freely changed without affecting the set.
- All elements can be freely garbage collected by Ruby. They will be removed from the set automatically.
- The order of elements in the set is non-deterministic. Insertion order is not preserved.
Note that Set is not inherently thread-safe. When accessing a Set from multiple threads or fibers, you MUST use a mutex or another locking mechanism.
Implementation Details
The various Ruby implementations and versions show quite diverse behavior in
their respective ObjectSpace::WeakMap implementations. To provide a
unified behavior on all implementations, we use different storage
strategies:
- Ruby (aka. MRI, aka. YARV) >= 3.3 has an
ObjectSpace::WeakMapwith weak keys and weak values and the ability to delete elements from it. This allows a straight-forward implementation in WeakKeysWithDelete. - Ruby (aka. MRI, aka. YARV) < 3.3 has an
ObjectSpace::WeakMapwith weak keys and weak values but does not allow to directly delete entries. We emulate this with special garbage-collectible values in WeakKeys. - JRuby >= 9.4.6.0 and TruffleRuby >= 22 have an
ObjectSpace::WeakMapwith strong keys and weak values. To allow a entries in anObjectSpace::WeakMapto be garbage collected, we can't use the actual object as a key. Instead, we use the element'sobject_idas a key. As theseObjectSpace::WeakMapobjects also do not allow to delete entries, we emulate deletion with special garbage-collectible values as above. This is implemented in StrongKeys. - JRuby < 9.4.6.0 has a similar
ObjectSpace::WeakMapas newer JRuby versions with strong keys and weak values. However generally in JRuby, Integer values (including object_ids) can have multiple different object representations in memory and are not necessarily equal to each other when used as keys in anObjectSpace::WeakMap. As a workaround we use an indirect implementation with a secondary lookup table for the keys in StrongSecondaryKeys.
The required strategy is selected automatically based in the running Ruby. The external behavior is the same for all implementations.
Defined Under Namespace
Modules: StrongKeys, StrongSecondaryKeys, WeakKeys, WeakKeysWithDelete
Constant Summary collapse
- STRATEGY =
We try to find the best implementation strategy based on the current Ruby engine and version. The chosen
STRATEGYis included into the Weak::Set class. [ Weak::Set::WeakKeysWithDelete, Weak::Set::WeakKeys, Weak::Set::StrongKeys, Weak::Set::StrongSecondaryKeys ].find(&:usable?)
Class Method Summary collapse
-
.[](*ary) ⇒ Weak::Set
A new weak set containing the given objects.
Instance Method Summary collapse
-
#&(enum) ⇒ Weak::Set
(also: #intersection)
A new weak set containing elements common to
selfand the given enumerable object. -
#-(enum) ⇒ Weak::Set
(also: #difference)
A new weak set built by duplicating
self, removing every element that appears in the given enumerable object from that. -
#<=>(other) ⇒ Integer?
0ifselfand the givensetcontain the same elements,-1/+1ifselfis a proper subset / superset of the givenset, ornilif they both have unique elements orsetis not a Set. -
#==(other) ⇒ Bool
Returns true if two weak sets are equal.
-
#[](obj) ⇒ Object?
The provided
objif it is included inself,nilotherwise. -
#^(enum) ⇒ Weak::Set
Returns a new weak set containing elements exclusive between
selfand the given enumerable object. -
#add(obj) ⇒ self
(also: #<<)
Adds the given object to the weak set and return
self. -
#add?(obj) ⇒ self?
Adds the given object to the weak set and returns
self. -
#clear ⇒ self
Removes all elements and returns
self. -
#clone(freeze: false) ⇒ Weak::Set
Set objects can't be frozen since this is not enforced by the underlying
ObjectSpace::WeakMapimplementation. -
#compare_by_identity ⇒ self
This method does nothing as we always compare elements by their object identity.
-
#compare_by_identity? ⇒ true
Always
truesince we always compare elements by their object identity. -
#delete(obj) ⇒ self
Deletes the given object from
selfand returnsself. -
#delete?(obj) ⇒ self?
Deletes the given object from
selfand returnsselfif it was present in the set. -
#delete_if {|element| ... } ⇒ self, Enumerator
Deletes every element of the weak set for which the given block block evaluates to a truethy value, and returns
self. -
#disjoint?(enum) ⇒ Bool
trueifselfand the givenenumhave no element in common. -
#each {|element| ... } ⇒ self, Enumerator
Calls the given block once for each live element in
self, passing that element as a parameter. -
#empty? ⇒ Boolean
trueifselfcontains no elements. -
#freeze ⇒ self
Set objects can't be frozen since this is not enforced by the underlying
ObjectSpace::WeakMapimplementation. -
#include?(obj) ⇒ Bool
(also: #===, #member?)
trueif the given object is included inself,falseotherwise. -
#initialize(enum = nil) {|element| ... } ⇒ Set
constructor
A new instance of Set.
-
#inspect ⇒ String
(also: #to_s)
A string containing a human-readable representation of the weak set, e.g.,
"#<Weak::Set {element1, element2, ...}>". -
#intersect?(enum) ⇒ Bool
trueifselfand the given enumerable object have at least one element in common,falseotherwise. -
#keep_if {|element| ... } ⇒ Enumerator, self
Deletes every element from
selffor which the given block evaluates to a falsey value. -
#merge(*enums) ⇒ self
Merges the elements of the given enumerable objects to the set and returns
self. -
#proper_subset?(other) ⇒ Bool
(also: #<)
trueifselfis a proper subset of the givenset,falseotherwise. -
#proper_superset?(other) ⇒ Bool
(also: #>)
trueifselfis a proper superset of the givenset,falseotherwise. -
#prune ⇒ self
(also: #reset)
Cleanup data structures from the set to remove data associated with deleted or garbage collected elements.
-
#reject! {|element| ... } ⇒ Enumerator, ...
Deletes every live element from
selffor which the given block evaluates to a truethy value. -
#replace(enum) ⇒ self
Replaces the contents of
selfwith the contents of the given enumerable object and returnsself. -
#select! {|element| ... } ⇒ Enumerator, ...
(also: #filter!)
Deletes every element from
selffor which the given block evaluates to a falsey value. -
#size ⇒ Integer
(also: #length)
The number of live elements in
self. -
#subset?(other) ⇒ Bool
(also: #<=)
trueifselfis a subset of the givenset,falseotherwise. -
#subtract(enum) ⇒ self
Deletes every element from
selfwhich appears in the given enumerable objectenumand returnsself. -
#superset?(other) ⇒ Bool
(also: #>=)
trueifselfis a superset of the givenset,falseotherwise. -
#to_a ⇒ Array
The live elements contained in
selfas anArray. -
#to_set ⇒ Set
The elements in
selfas a regularSetwith strong object references. -
#|(enum) ⇒ Weak::Set
(also: #+, #union)
A new weak set built by merging
selfand the elements of the given enumerable object.
Constructor Details
#initialize(enum = nil) {|element| ... } ⇒ Set
Returns a new instance of Set.
232 233 234 235 236 237 238 239 240 241 242 243 244 245 |
# File 'lib/weak/set.rb', line 232 def initialize(enum = nil) clear return if enum.nil? if block_given? do_with_enum(enum) do |obj| add yield(obj) end else do_with_enum(enum) do |obj| add obj end end end |
Class Method Details
.[](*ary) ⇒ Weak::Set
Returns a new weak set containing the given objects.
222 223 224 |
# File 'lib/weak/set.rb', line 222 def self.[](*ary) new(ary) end |
Instance Method Details
#&(enum) ⇒ Weak::Set Also known as: intersection
Weak::Set does not test member equality with == or eql?.
Instead, it always checks strict object equality, so that, e.g.,
different strings are not considered equal, even if they may contain
the same string content.
Returns a new weak set containing elements common to self
and the given enumerable object.
292 293 294 295 296 297 298 |
# File 'lib/weak/set.rb', line 292 def &(enum) new_set = self.class.new do_with_enum(enum) do |obj| new_set.add(obj) if include?(obj) end new_set end |
#-(enum) ⇒ Weak::Set Also known as: difference
Weak::Set does not test member equality with == or eql?.
Instead, it always checks strict object equality, so that, e.g.,
different strings are not considered equal, even if they may contain
the same string content.
Returns a new weak set built by duplicating self, removing
every element that appears in the given enumerable object from that.
279 280 281 |
# File 'lib/weak/set.rb', line 279 def -(enum) dup.subtract(enum) end |
#<=>(other) ⇒ Integer?
Weak::Set does not test member equality with == or eql?.
Instead, it always checks strict object equality, so that, e.g.,
different strings are not considered equal, even if they may contain
the same string content.
Returns 0 if self and the given set contain the same
elements, -1 / +1 if self is a proper subset / superset of the
given set, or nil if they both have unique elements or set is not
a Weak::Set.
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 |
# File 'lib/weak/set.rb', line 307 def <=>(other) return unless Weak::Set === other return 0 if equal?(other) other_ary = other.to_a own_ary = to_a case own_ary.size <=> other_ary.size when -1 -1 if own_ary.all?(other) when 1 1 if other_ary.all?(self) else 0 if own_ary.all?(other) end end |
#==(other) ⇒ Bool
Returns true if two weak sets are equal. The equality of each couple of elements is defined according to strict object equality so that, e.g., different strings are not equal, even if they may contain the same data.
335 336 337 338 339 340 341 342 343 344 |
# File 'lib/weak/set.rb', line 335 def ==(other) return true if equal?(other) return false unless Weak::Set === other other_ary = other.to_a own_ary = to_a return false unless own_ary.size == other_ary.size own_ary.all?(other) end |
#[](obj) ⇒ Object?
Weak::Set does not test member equality with == or eql?.
Instead, it always checks strict object equality, so that, e.g.,
different strings are not considered equal, even if they may contain
the same string content.
Returns the provided obj if it is included in self,
nil otherwise.
372 373 374 |
# File 'lib/weak/set.rb', line 372 def [](obj) obj if include?(obj) end |
#^(enum) ⇒ Weak::Set
Weak::Set does not test member equality with == or eql?.
Instead, it always checks strict object equality, so that, e.g.,
different strings are not considered equal, even if they may contain
the same string content.
Returns a new weak set containing elements exclusive between self and
the given enumerable object. (set ^ enum) is equivalent to
((set | enum) - (set & enum)).
357 358 359 360 361 362 363 364 365 |
# File 'lib/weak/set.rb', line 357 def ^(enum) return dup if enum.nil? new_set = self.class.new.merge(enum) each do |obj| new_set.add(obj) unless new_set.delete?(obj) end new_set end |
#add(obj) ⇒ self Also known as: <<
Adds the given object to the weak set and return self. Use #merge to
add many elements at once.
In contrast to other "regular" objects, we will not retain a strong reference to the added object. Unless some other live objects still references the object, it will eventually be garbage-collected.
|
|
# File 'lib/weak/set.rb', line 181
|
#add?(obj) ⇒ self?
Weak::Set does not test member equality with == or eql?.
Instead, it always checks strict object equality, so that, e.g.,
different strings are not considered equal, even if they may contain
the same string content.
Adds the given object to the weak set and returns self. If the object is
already in the set, returns nil.
388 389 390 |
# File 'lib/weak/set.rb', line 388 def add?(obj) add(obj) unless include?(obj) end |
#clear ⇒ self
Removes all elements and returns self
|
|
# File 'lib/weak/set.rb', line 184
|
#clone(freeze: false) ⇒ Weak::Set
Weak::Set objects can't be frozen since this is not enforced by the
underlying ObjectSpace::WeakMap implementation. Thus, we try to signal
this by not actually setting the frozen? flag and ignoring attempts to
freeze us with just a warning.
401 402 403 404 405 |
# File 'lib/weak/set.rb', line 401 def clone(freeze: false) warn("Can't freeze #{self.class}") if freeze super(freeze: false) end |
#compare_by_identity ⇒ self
This method does nothing as we always compare elements by their object identity.
411 412 413 |
# File 'lib/weak/set.rb', line 411 def compare_by_identity self end |
#compare_by_identity? ⇒ true
Returns always true since we always compare elements by their
object identity.
417 418 419 |
# File 'lib/weak/set.rb', line 417 def compare_by_identity? true end |
#delete(obj) ⇒ self
Weak::Set does not test member equality with == or eql?.
Instead, it always checks strict object equality, so that, e.g.,
different strings are not considered equal, even if they may contain
the same string content.
Deletes the given object from self and returns self. Use #subtract
to delete many items at once.
427 428 429 430 |
# File 'lib/weak/set.rb', line 427 def delete(obj) delete?(obj) self end |
#delete?(obj) ⇒ self?
Weak::Set does not test member equality with == or eql?.
Instead, it always checks strict object equality, so that, e.g.,
different strings are not considered equal, even if they may contain
the same string content.
Deletes the given object from self and returns self if it was
present in the set. If the object was not in the set, returns nil.
|
|
# File 'lib/weak/set.rb', line 187
|
#delete_if {|element| ... } ⇒ self, Enumerator
Deletes every element of the weak set for which the given block block
evaluates to a truethy value, and returns self. Returns an Enumerator
if no block is given.
441 442 443 444 445 446 447 448 |
# File 'lib/weak/set.rb', line 441 def delete_if(&block) return enum_for(__method__) { size } unless block_given? each do |obj| delete?(obj) if yield(obj) end self end |
#disjoint?(enum) ⇒ Bool
Weak::Set does not test member equality with == or eql?.
Instead, it always checks strict object equality, so that, e.g.,
different strings are not considered equal, even if they may contain
the same string content.
Returns true if self and the given enum have no element in
common. This method is the opposite of #intersect?.
454 455 456 |
# File 'lib/weak/set.rb', line 454 def disjoint?(enum) !intersect?(enum) end |
#each {|element| ... } ⇒ self, Enumerator
Calls the given block once for each live element in self, passing that
element as a parameter. Returns the weak set itself.
If no block is given, an Enumerator is returned instead.
|
|
# File 'lib/weak/set.rb', line 190
|
#empty? ⇒ Boolean
Returns true if self contains no elements.
459 460 461 |
# File 'lib/weak/set.rb', line 459 def empty? size == 0 end |
#freeze ⇒ self
Weak::Set objects can't be frozen since this is not enforced by the
underlying ObjectSpace::WeakMap implementation. Thus, we try to signal
this by not actually setting the frozen? flag and ignoring attempts to
freeze us with just a warning.
469 470 471 472 |
# File 'lib/weak/set.rb', line 469 def freeze warn("Can't freeze #{self.class}") self end |
#include?(obj) ⇒ Bool Also known as: ===, member?
Weak::Set does not test member equality with == or eql?.
Instead, it always checks strict object equality, so that, e.g.,
different strings are not considered equal, even if they may contain
the same string content.
Returns true if the given object is included in self, false
otherwise.
|
|
# File 'lib/weak/set.rb', line 193
|
#inspect ⇒ String Also known as: to_s
Returns a string containing a human-readable representation of
the weak set, e.g., "#<Weak::Set {element1, element2, ...}>".
476 477 478 479 480 481 482 483 484 485 486 487 |
# File 'lib/weak/set.rb', line 476 def inspect object_ids = (Thread.current[INSPECT_KEY] ||= []) return "#<#{self.class} {...}>" if object_ids.include?(object_id) object_ids << object_id begin elements = to_a.sort_by!(&:__id__).inspect[1..-2] "#<#{self.class} {#{elements}}>" ensure object_ids.pop end end |
#intersect?(enum) ⇒ Bool
Weak::Set does not test member equality with == or eql?.
Instead, it always checks strict object equality, so that, e.g.,
different strings are not considered equal, even if they may contain
the same string content.
Returns true if self and the given enumerable object have at
least one element in common, false otherwise.
500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 |
# File 'lib/weak/set.rb', line 500 def intersect?(enum) case enum when Weak::Set enum_ary = enum.to_a own_ary = to_a if own_ary.size < enum_ary.size own_ary.any?(enum) else enum_ary.any?(self) end else enumerable(enum).any?(self) end end |
#keep_if {|element| ... } ⇒ Enumerator, self
Deletes every element from self for which the given block evaluates to
a falsey value.
If no block is given, an Enumerator is returned instead.
527 528 529 530 531 532 533 534 |
# File 'lib/weak/set.rb', line 527 def keep_if(&block) return enum_for(__method__) { size } unless block_given? each do |obj| delete?(obj) unless yield(obj) end self end |
#merge(*enums) ⇒ self
Merges the elements of the given enumerable objects to the set and returns
self
541 542 543 544 545 546 547 548 |
# File 'lib/weak/set.rb', line 541 def merge(*enums, **nil) enums.each do |enum| do_with_enum(enum) do |obj| add(obj) end end self end |
#proper_subset?(other) ⇒ Bool Also known as: <
Returns true if self is a proper subset of the given set,
false otherwise.
571 572 573 574 575 576 577 578 579 580 581 |
# File 'lib/weak/set.rb', line 571 def proper_subset?(other) if Weak::Set === other other_ary = other.to_a own_ary = to_a return false unless own_ary.size < other_ary.size own_ary.all?(other) else raise ArgumentError, "value must be a weak set" end end |
#proper_superset?(other) ⇒ Bool Also known as: >
Weak::Set does not test member equality with == or eql?.
Instead, it always checks strict object equality, so that, e.g.,
different strings are not considered equal, even if they may contain
the same string content.
Returns true if self is a proper superset of the given set,
false otherwise.
589 590 591 592 593 594 595 596 597 598 599 |
# File 'lib/weak/set.rb', line 589 def proper_superset?(other) if Weak::Set === other other_ary = other.to_a own_ary = to_a return false unless own_ary.size > other_ary.size other_ary.all?(self) else raise ArgumentError, "value must be a weak set" end end |
#prune ⇒ self Also known as: reset
Cleanup data structures from the set to remove data associated with deleted or garbage collected elements. This method may be called automatically for some Weak::Set operations.
|
|
# File 'lib/weak/set.rb', line 196
|
#reject! {|element| ... } ⇒ Enumerator, ...
Deletes every live element from self for which the given block
evaluates to a truethy value.
Equivalent to #delete_if, but returns nil if no changes were made.
If no block is given, an Enumerator is returned instead.
deleted, or an Enumerator if no block was given.
see #delete_if
615 616 617 618 619 620 621 622 623 624 |
# File 'lib/weak/set.rb', line 615 def reject!(&block) return enum_for(__method__) { size } unless block_given? deleted_anything = false each do |obj| deleted_anything = true if yield(obj) && delete?(obj) end self if deleted_anything end |
#replace(enum) ⇒ self
Replaces the contents of self with the contents of the given
enumerable object and returns self.
|
|
# File 'lib/weak/set.rb', line 202
|
#select! {|element| ... } ⇒ Enumerator, ... Also known as: filter!
Deletes every element from self for which the given block evaluates to
a falsey value.
Equivalent to #keep_if, but returns nil if no changes were made.
If no block is given, an Enumerator is returned instead.
639 640 641 642 643 644 645 646 647 648 |
# File 'lib/weak/set.rb', line 639 def select!(&block) return enum_for(__method__) { size } unless block_given? deleted_anything = false each do |obj| deleted_anything = true if !yield(obj) && delete?(obj) end self if deleted_anything end |
#size ⇒ Integer Also known as: length
Returns the number of live elements in self.
|
|
# File 'lib/weak/set.rb', line 199
|
#subset?(other) ⇒ Bool Also known as: <=
Weak::Set does not test member equality with == or eql?.
Instead, it always checks strict object equality, so that, e.g.,
different strings are not considered equal, even if they may contain
the same string content.
Returns true if self is a subset of the given set, false
otherwise.
656 657 658 659 660 661 662 663 664 665 666 |
# File 'lib/weak/set.rb', line 656 def subset?(other) if Weak::Set === other other_ary = other.to_a own_ary = to_a return false unless own_ary.size <= other_ary.size own_ary.all?(other) else raise ArgumentError, "value must be a weak set" end end |
#subtract(enum) ⇒ self
Deletes every element from self which appears in the given enumerable
object enum and returns self.
674 675 676 677 678 679 |
# File 'lib/weak/set.rb', line 674 def subtract(enum) do_with_enum(enum) do |obj| delete?(obj) end self end |
#superset?(other) ⇒ Bool Also known as: >=
Returns true if self is a superset of the given set, false
otherwise.
685 686 687 688 689 690 691 692 693 694 695 |
# File 'lib/weak/set.rb', line 685 def superset?(other) if Weak::Set === other other_ary = other.to_a own_ary = to_a return false unless own_ary.size >= other_ary.size other_ary.all?(self) else raise ArgumentError, "value must be a weak set" end end |
#to_a ⇒ Array
The order of elements on the returned Array is
non-deterministic. We do not preserve preserve insertion order.
Returns the live elements contained in self as an Array.
|
|
# File 'lib/weak/set.rb', line 205
|
#to_set ⇒ Set
The returned set is configured to compare elements by their object
identity, similar to a Weak::Set.
Returns the elements in self as a regular Set with strong object
references.
702 703 704 705 706 707 708 |
# File 'lib/weak/set.rb', line 702 def to_set set = ::Set.new.compare_by_identity each do |obj| set.add(obj) end set end |
#|(enum) ⇒ Weak::Set Also known as: +, union
Weak::Set does not test member equality with == or eql?.
Instead, it always checks strict object equality, so that, e.g.,
different strings are not considered equal, even if they may contain
the same string content.
Returns a new weak set built by merging self and the elements
of the given enumerable object.
261 262 263 264 265 266 267 |
# File 'lib/weak/set.rb', line 261 def |(enum) new_set = dup do_with_enum(enum) do |obj| new_set.add(obj) end new_set end |