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#hash
as 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::WeakMap
with 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::WeakMap
with 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::WeakMap
with strong keys and weak values. To allow a entries in anObjectSpace::WeakMap
to be garbage collected, we can't use the actual object as a key. Instead, we use the element'sobject_id
as a key. As theseObjectSpace::WeakMap
objects 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::WeakMap
as 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
STRATEGY
is 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
self
and 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?
0
ifself
and the givenset
contain the same elements,-1
/+1
ifself
is a proper subset / superset of the givenset
, ornil
if they both have unique elements orset
is not a Set. -
#==(other) ⇒ Bool
Returns true if two weak sets are equal.
-
#[](obj) ⇒ Object?
The provided
obj
if it is included inself
,nil
otherwise. -
#^(enum) ⇒ Weak::Set
Returns a new weak set containing elements exclusive between
self
and 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::WeakMap
implementation. -
#compare_by_identity ⇒ self
This method does nothing as we always compare elements by their object identity.
-
#compare_by_identity? ⇒ true
Always
true
since we always compare elements by their object identity. -
#delete(obj) ⇒ self
Deletes the given object from
self
and returnsself
. -
#delete?(obj) ⇒ self?
Deletes the given object from
self
and returnsself
if 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
true
ifself
and the givenenum
have 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
true
ifself
contains no elements. -
#freeze ⇒ self
Set objects can't be frozen since this is not enforced by the underlying
ObjectSpace::WeakMap
implementation. -
#include?(obj) ⇒ Bool
(also: #===, #member?)
true
if the given object is included inself
,false
otherwise. -
#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
true
ifself
and the given enumerable object have at least one element in common,false
otherwise. -
#keep_if {|element| ... } ⇒ Enumerator, self
Deletes every element from
self
for 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: #<)
true
ifself
is a proper subset of the givenset
,false
otherwise. -
#proper_superset?(other) ⇒ Bool
(also: #>)
true
ifself
is a proper superset of the givenset
,false
otherwise. -
#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
self
for which the given block evaluates to a truethy value. -
#replace(enum) ⇒ self
Replaces the contents of
self
with the contents of the given enumerable object and returnsself
. -
#select! {|element| ... } ⇒ Enumerator, ...
(also: #filter!)
Deletes every element from
self
for which the given block evaluates to a falsey value. -
#size ⇒ Integer
(also: #length)
The number of live elements in
self
. -
#subset?(other) ⇒ Bool
(also: #<=)
true
ifself
is a subset of the givenset
,false
otherwise. -
#subtract(enum) ⇒ self
Deletes every element from
self
which appears in the given enumerable objectenum
and returnsself
. -
#superset?(other) ⇒ Bool
(also: #>=)
true
ifself
is a superset of the givenset
,false
otherwise. -
#to_a ⇒ Array
The live elements contained in
self
as anArray
. -
#to_set ⇒ Set
The elements in
self
as a regularSet
with strong object references. -
#|(enum) ⇒ Weak::Set
(also: #+, #union)
A new weak set built by merging
self
and 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 |