From 60e0bc87a228ad5d66c3954e967cec65f6e34171 Mon Sep 17 00:00:00 2001 From: nathan Date: Wed, 4 Apr 2018 15:30:04 +0200 Subject: [PATCH] Updated and added more LockFree maps --- .../util/collections/ConcurrentIterator.java | 42 ++-- .../util/collections/LockFreeHashMap.java | 45 +++- .../util/collections/LockFreeIntBiMap.java | 40 ++-- .../util/collections/LockFreeIntMap.java | 49 ++-- .../collections/LockFreeObjectIntBiMap.java | 42 ++-- .../collections/LockFreeObjectIntMap.java | 210 ++++++++++++++++++ .../util/collections/LockFreeObjectMap.java | 192 ++++++++++++++++ src/dorkbox/util/collections/LockFreeSet.java | 26 ++- .../util/collections/ObjectIntMap.java | 25 --- 9 files changed, 550 insertions(+), 121 deletions(-) create mode 100644 src/dorkbox/util/collections/LockFreeObjectIntMap.java create mode 100644 src/dorkbox/util/collections/LockFreeObjectMap.java diff --git a/src/dorkbox/util/collections/ConcurrentIterator.java b/src/dorkbox/util/collections/ConcurrentIterator.java index ec6e8c0..9a9c6da 100644 --- a/src/dorkbox/util/collections/ConcurrentIterator.java +++ b/src/dorkbox/util/collections/ConcurrentIterator.java @@ -16,12 +16,13 @@ package dorkbox.util.collections; -import com.esotericsoftware.kryo.util.IdentityMap; -import dorkbox.util.Property; - import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; +import com.esotericsoftware.kryo.util.IdentityMap; + +import dorkbox.util.Property; + /** * @author dorkbox, llc */ @@ -37,7 +38,7 @@ class ConcurrentIterator { private final int ID = ID_COUNTER.getAndIncrement(); // This is only touched by a single thread, maintains a map of entries for FAST lookup during remove. - private final IdentityMap entries = new IdentityMap(32, LOAD_FACTOR); + private final IdentityMap entries = new IdentityMap(32, LOAD_FACTOR); // this is still inside the single-writer, and can use the same techniques as subscription manager (for thread safe publication) @SuppressWarnings("FieldCanBeLocal") @@ -53,7 +54,10 @@ class ConcurrentIterator { ConcurrentIterator() { } - // called on shutdown for GC purposes + /** + * single writer principle! + * called from within SYNCHRONIZE + */ public final void clear() { this.entries.clear(); @@ -66,12 +70,12 @@ class ConcurrentIterator { * * @param listener the object that will receive messages during publication */ - public - void add(final Object listener) { - ConcurrentEntry head = headREF.get(this); + public synchronized + void add(final T listener) { + ConcurrentEntry head = headREF.get(this); if (!entries.containsKey(listener)) { - head = new ConcurrentEntry(listener, head); + head = new ConcurrentEntry(listener, head); entries.put(listener, head); headREF.lazySet(this, head); @@ -84,12 +88,12 @@ class ConcurrentIterator { * * @param listener the object that will NO LONGER receive messages during publication */ - public - void remove(final Object listener) { - ConcurrentEntry concurrentEntry = entries.get(listener); + public synchronized + boolean remove(final T listener) { + ConcurrentEntry concurrentEntry = entries.get(listener); if (concurrentEntry != null) { - ConcurrentEntry head = headREF.get(this); + ConcurrentEntry head = headREF.get(this); if (concurrentEntry == head) { // if it was second, now it's first @@ -102,7 +106,19 @@ class ConcurrentIterator { headREF.lazySet(this, head); this.entries.remove(listener); + return true; } + + return false; + } + + /** + * single writer principle! + * called from within SYNCHRONIZE + */ + public synchronized + int size() { + return entries.size; } @Override diff --git a/src/dorkbox/util/collections/LockFreeHashMap.java b/src/dorkbox/util/collections/LockFreeHashMap.java index e711f2f..53f5679 100644 --- a/src/dorkbox/util/collections/LockFreeHashMap.java +++ b/src/dorkbox/util/collections/LockFreeHashMap.java @@ -35,7 +35,7 @@ import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; public final class LockFreeHashMap implements Map, Cloneable, Serializable { // Recommended for best performance while adhering to the "single writer principle". Must be static-final - private static final AtomicReferenceFieldUpdater deviceREF = AtomicReferenceFieldUpdater.newUpdater( + private static final AtomicReferenceFieldUpdater mapREF = AtomicReferenceFieldUpdater.newUpdater( LockFreeHashMap.class, HashMap.class, "hashMap"); @@ -102,47 +102,47 @@ class LockFreeHashMap implements Map, Cloneable, Serializable { public Map getMap() { // use the SWP to get a lock-free get of the map. It's values are only valid at the moment this method is called. - return Collections.unmodifiableMap(deviceREF.get(this)); + return Collections.unmodifiableMap(mapREF.get(this)); } @Override public int size() { // use the SWP to get a lock-free get of the value - return deviceREF.get(this) - .size(); + return mapREF.get(this) + .size(); } @Override public boolean isEmpty() { // use the SWP to get a lock-free get of the value - return deviceREF.get(this) - .isEmpty(); + return mapREF.get(this) + .isEmpty(); } @Override public boolean containsKey(final Object key) { // use the SWP to get a lock-free get of the value - return deviceREF.get(this) - .containsKey(key); + return mapREF.get(this) + .containsKey(key); } @Override public boolean containsValue(final Object value) { // use the SWP to get a lock-free get of the value - return deviceREF.get(this) - .containsValue(value); + return mapREF.get(this) + .containsValue(value); } @Override public V get(final Object key) { // use the SWP to get a lock-free get of the value - return (V) deviceREF.get(this) - .get(key); + return (V) mapREF.get(this) + .get(key); } @Override @@ -186,4 +186,25 @@ class LockFreeHashMap implements Map, Cloneable, Serializable { Set> entrySet() { return getMap().entrySet(); } + + @Override + public + boolean equals(final Object o) { + return mapREF.get(this) + .equals(o); + } + + @Override + public + int hashCode() { + return mapREF.get(this) + .hashCode(); + } + + @Override + public + String toString() { + return mapREF.get(this) + .toString(); + } } diff --git a/src/dorkbox/util/collections/LockFreeIntBiMap.java b/src/dorkbox/util/collections/LockFreeIntBiMap.java index 05d8594..d8a0976 100644 --- a/src/dorkbox/util/collections/LockFreeIntBiMap.java +++ b/src/dorkbox/util/collections/LockFreeIntBiMap.java @@ -42,15 +42,15 @@ import dorkbox.util.collections.IntMap.Keys; public class LockFreeIntBiMap { // Recommended for best performance while adhering to the "single writer principle". Must be static-final - private static final AtomicReferenceFieldUpdater forwardREF = - AtomicReferenceFieldUpdater.newUpdater(LockFreeIntBiMap.class, - IntMap.class, - "forwardHashMap"); + private static final AtomicReferenceFieldUpdater forwardREF = AtomicReferenceFieldUpdater.newUpdater( + LockFreeIntBiMap.class, + IntMap.class, + "forwardHashMap"); - private static final AtomicReferenceFieldUpdater reverseREF = - AtomicReferenceFieldUpdater.newUpdater(LockFreeIntBiMap.class, - ObjectIntMap.class, - "reverseHashMap"); + private static final AtomicReferenceFieldUpdater reverseREF = AtomicReferenceFieldUpdater.newUpdater( + LockFreeIntBiMap.class, + ObjectIntMap.class, + "reverseHashMap"); private volatile IntMap forwardHashMap; private volatile ObjectIntMap reverseHashMap; @@ -377,32 +377,20 @@ class LockFreeIntBiMap { .size; } + /** + * Identity equals only! + */ @Override public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - final LockFreeIntBiMap that = (LockFreeIntBiMap) o; - - if (defaultReturnValue != that.defaultReturnValue) { - return false; - } - if (!forwardHashMap.equals(that.forwardHashMap)) { - return false; - } - return reverseHashMap.equals(that.reverseHashMap); + return this == o; } @Override public int hashCode() { - int result = forwardHashMap.hashCode(); - result = 31 * result + reverseHashMap.hashCode(); + int result = forwardREF.get(this).hashCode(); + result = 31 * result + reverseREF.get(this).hashCode(); result = 31 * result + defaultReturnValue; return result; } diff --git a/src/dorkbox/util/collections/LockFreeIntMap.java b/src/dorkbox/util/collections/LockFreeIntMap.java index 63be087..b7ca440 100644 --- a/src/dorkbox/util/collections/LockFreeIntMap.java +++ b/src/dorkbox/util/collections/LockFreeIntMap.java @@ -46,7 +46,7 @@ import dorkbox.util.collections.IntMap.Values; public final class LockFreeIntMap implements Cloneable, Serializable { // Recommended for best performance while adhering to the "single writer principle". Must be static-final - private static final AtomicReferenceFieldUpdater deviceREF = AtomicReferenceFieldUpdater.newUpdater( + private static final AtomicReferenceFieldUpdater mapREF = AtomicReferenceFieldUpdater.newUpdater( LockFreeIntMap.class, IntMap.class, "map"); @@ -98,22 +98,22 @@ class LockFreeIntMap implements Cloneable, Serializable { public int size() { // use the SWP to get a lock-free get of the value - return deviceREF.get(this) + return mapREF.get(this) .size; } public boolean isEmpty() { // use the SWP to get a lock-free get of the value - return deviceREF.get(this) + return mapREF.get(this) .size == 0; } public boolean containsKey(final int key) { // use the SWP to get a lock-free get of the value - return deviceREF.get(this) - .containsKey(key); + return mapREF.get(this) + .containsKey(key); } /** @@ -126,15 +126,15 @@ class LockFreeIntMap implements Cloneable, Serializable { public boolean containsValue(final Object value, boolean identity) { // use the SWP to get a lock-free get of the value - return deviceREF.get(this) - .containsValue(value, identity); + return mapREF.get(this) + .containsValue(value, identity); } public V get(final int key) { // use the SWP to get a lock-free get of the value - return (V) deviceREF.get(this) - .get(key); + return (V) mapREF.get(this) + .get(key); } public synchronized @@ -157,13 +157,36 @@ class LockFreeIntMap implements Cloneable, Serializable { map.clear(); } + /** + * Identity equals only! + */ + @Override + public + boolean equals(final Object o) { + return this == o; + } + + @Override + public + int hashCode() { + return mapREF.get(this) + .hashCode(); + } + + @Override + public + String toString() { + return mapREF.get(this) + .toString(); + } + /** * DO NOT MODIFY THE MAP VIA THIS! It will result in unknown object visibility! */ public Keys keySet() { - return deviceREF.get(this) - .keys(); + return mapREF.get(this) + .keys(); } /** @@ -171,7 +194,7 @@ class LockFreeIntMap implements Cloneable, Serializable { */ public Values values() { - return deviceREF.get(this) - .values(); + return mapREF.get(this) + .values(); } } diff --git a/src/dorkbox/util/collections/LockFreeObjectIntBiMap.java b/src/dorkbox/util/collections/LockFreeObjectIntBiMap.java index 917f041..cf40ef0 100644 --- a/src/dorkbox/util/collections/LockFreeObjectIntBiMap.java +++ b/src/dorkbox/util/collections/LockFreeObjectIntBiMap.java @@ -42,15 +42,15 @@ import dorkbox.util.collections.IntMap.Keys; public class LockFreeObjectIntBiMap { // Recommended for best performance while adhering to the "single writer principle". Must be static-final - private static final AtomicReferenceFieldUpdater forwardREF = - AtomicReferenceFieldUpdater.newUpdater(LockFreeObjectIntBiMap.class, - ObjectIntMap.class, - "forwardHashMap"); + private static final AtomicReferenceFieldUpdater forwardREF = AtomicReferenceFieldUpdater.newUpdater( + LockFreeObjectIntBiMap.class, + ObjectIntMap.class, + "forwardHashMap"); - private static final AtomicReferenceFieldUpdater reverseREF = - AtomicReferenceFieldUpdater.newUpdater(LockFreeObjectIntBiMap.class, - IntMap.class, - "reverseHashMap"); + private static final AtomicReferenceFieldUpdater reverseREF = AtomicReferenceFieldUpdater.newUpdater( + LockFreeObjectIntBiMap.class, + IntMap.class, + "reverseHashMap"); private volatile ObjectIntMap forwardHashMap; private volatile IntMap reverseHashMap; @@ -353,32 +353,20 @@ class LockFreeObjectIntBiMap { .size; } + /** + * Identity equals only! + */ @Override public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - final LockFreeObjectIntBiMap that = (LockFreeObjectIntBiMap) o; - - if (defaultReturnValue != that.defaultReturnValue) { - return false; - } - if (!forwardHashMap.equals(that.forwardHashMap)) { - return false; - } - return reverseHashMap.equals(that.reverseHashMap); + return this == o; } @Override public int hashCode() { - int result = forwardHashMap.hashCode(); - result = 31 * result + reverseHashMap.hashCode(); + int result = forwardREF.get(this).hashCode(); + result = 31 * result + reverseREF.get(this).hashCode(); result = 31 * result + defaultReturnValue; return result; } @@ -388,7 +376,7 @@ class LockFreeObjectIntBiMap { String toString() { StringBuilder builder = new StringBuilder("LockFreeObjectIntBiMap {"); - Iterator keys = keys(); + Iterator keys = keys(); Keys values = values(); while (keys.hasNext()) { diff --git a/src/dorkbox/util/collections/LockFreeObjectIntMap.java b/src/dorkbox/util/collections/LockFreeObjectIntMap.java new file mode 100644 index 0000000..2946b24 --- /dev/null +++ b/src/dorkbox/util/collections/LockFreeObjectIntMap.java @@ -0,0 +1,210 @@ +/* + * Copyright 2018 dorkbox, llc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dorkbox.util.collections; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; + +/** + * This class uses the "single-writer-principle" for lock-free publication. + * + * Since there are only 2 methods to guarantee that modifications can only be called one-at-a-time (either it is only called by + * one thread, or only one thread can access it at a time) -- we chose the 2nd option -- and use 'synchronized' to make sure that only + * one thread can access this modification methods at a time. Getting or checking the presence of values can then happen in a lock-free + * manner. + * + * According to my benchmarks, this is approximately 25% faster than ConcurrentHashMap for (all types of) reads, and a lot slower for + * contended writes. + * + * This data structure is for many-read/few-write scenarios + */ +public +class LockFreeObjectIntMap { + // Recommended for best performance while adhering to the "single writer principle". Must be static-final + private static final AtomicReferenceFieldUpdater mapREF = AtomicReferenceFieldUpdater.newUpdater( + LockFreeObjectIntMap.class, + ObjectIntMap.class, + "map"); + + private volatile ObjectIntMap map; + + private final int defaultReturnValue; + + // synchronized is used here to ensure the "single writer principle", and make sure that ONLY one thread at a time can enter this + // section. Because of this, we can have unlimited reader threads all going at the same time, without contention (which is our + // use-case 99% of the time) + + /** + * Creates a new map using @{link Integer#MIN_VALUE}. + */ + public + LockFreeObjectIntMap() { + this(Integer.MIN_VALUE); + } + + /** + * The default return value is used for various get/put operations on the ObjectIntMap. + * + * @param defaultReturnValue value used for various get/put operations on the ObjectIntMap. + */ + public + LockFreeObjectIntMap(int defaultReturnValue) { + this(new ObjectIntMap(), defaultReturnValue); + } + + /** + * The default return value is used for various get/put operations on the ObjectIntMap. + * + * @param defaultReturnValue value used for various get/put operations on the ObjectIntMap. + */ + LockFreeObjectIntMap(ObjectIntMap forwardHashMap, int defaultReturnValue) { + this.map = forwardHashMap; + this.defaultReturnValue = defaultReturnValue; + } + + /** + * Removes all of the mappings from this map. + * + * The map will be empty after this call returns. + */ + public synchronized + void clear() { + map.clear(); + } + + public synchronized + int put(final V key, final int value) { + int prevForwardValue = this.map.get(key, defaultReturnValue); + this.map.put(key, value); + + return prevForwardValue; + } + + /** + * Copies all of the mappings from the specified map to this map. + * These mappings will replace any mappings that this map had for + * any of the keys currently in the specified map. + * + * @param hashMap mappings to be stored in this map + * + * @throws NullPointerException if the specified map is null + */ + public synchronized + void putAll(final Map hashMap) throws IllegalArgumentException { + try { + ObjectIntMap map = this.map; + for (Map.Entry entry : hashMap.entrySet()) { + V key = entry.getKey(); + Integer value = entry.getValue(); + + map.put(key, value); + } + } catch (IllegalArgumentException e) { + // do nothing if there is an exception + throw e; + } + } + + /** + * Removes the mapping for the specified key from this map if present. + * + * @param key key whose mapping is to be removed from the map + * + * @return the previous value associated with key, or + * defaultReturnValue if there was no mapping for key. + * (A defaultReturnValue return can also indicate that the map + * previously associated defaultReturnValue with key.) + */ + public synchronized + int remove(final V key) { + int value = map.remove(key, defaultReturnValue); + return value; + } + + + /** + * Returns the value to which the specified key is mapped, + * or {@code defaultReturnValue} if this map contains no mapping for the key. + *

+ *

More formally, if this map contains a mapping from a key + * {@code k} to a value {@code v} such that {@code (key==null ? k==null : + * key.equals(k))}, then this method returns {@code v}; otherwise + * it returns {@code defaultReturnValue}. (There can be at most one such mapping.) + *

+ *

A return value of {@code defaultReturnValue} does not necessarily + * indicate that the map contains no mapping for the key; it's also + * possible that the map explicitly maps the key to {@code null}. + * The {@link HashMap#containsKey containsKey} operation may be used to + * distinguish these two cases. + * + * @see #put(Object, int) + */ + @SuppressWarnings("unchecked") + public + int get(final V key) { + // use the SWP to get a lock-free get of the value + return mapREF.get(this).get(key, defaultReturnValue); + } + + /** + * Returns true if this map contains no key-value mappings. + * + * @return true if this map contains no key-value mappings + */ + public + boolean isEmpty() { + // use the SWP to get a lock-free get of the value + return mapREF.get(this) + .size == 0; + } + + /** + * Returns the number of key-value mappings in this map. If the + * map contains more than Integer.MAX_VALUE elements, returns + * Integer.MAX_VALUE. + * + * @return the number of key-value mappings in this map + */ + public + int size() { + // use the SWP to get a lock-free get of the value + return mapREF.get(this) + .size; + } + + /** + * Identity equals only! + */ + @Override + public + boolean equals(final Object o) { + return this == o; + } + + @Override + public + int hashCode() { + return mapREF.get(this).hashCode(); + } + + @Override + public + String toString() { + return mapREF.get(this) + .toString(); + } +} diff --git a/src/dorkbox/util/collections/LockFreeObjectMap.java b/src/dorkbox/util/collections/LockFreeObjectMap.java new file mode 100644 index 0000000..2a633fc --- /dev/null +++ b/src/dorkbox/util/collections/LockFreeObjectMap.java @@ -0,0 +1,192 @@ +/* + * Copyright 2018 dorkbox, llc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dorkbox.util.collections; + +import java.io.Serializable; +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; + +import com.esotericsoftware.kryo.util.ObjectMap; +import com.esotericsoftware.kryo.util.ObjectMap.Entries; +import com.esotericsoftware.kryo.util.ObjectMap.Keys; +import com.esotericsoftware.kryo.util.ObjectMap.Values; + +/** + * This class uses the "single-writer-principle" for lock-free publication. + *

+ * Since there are only 2 methods to guarantee that modifications can only be called one-at-a-time (either it is only called by + * one thread, or only one thread can access it at a time) -- we chose the 2nd option -- and use 'synchronized' to make sure that only + * one thread can access this modification methods at a time. Getting or checking the presence of values can then happen in a lock-free + * manner. + *

+ * According to my benchmarks, this is approximately 25% faster than ConcurrentHashMap for (all types of) reads, and a lot slower for + * contended writes. + *

+ * This data structure is for many-read/few-write scenarios + */ +public final +class LockFreeObjectMap implements Cloneable, Serializable { + // Recommended for best performance while adhering to the "single writer principle". Must be static-final + private static final AtomicReferenceFieldUpdater mapREF = AtomicReferenceFieldUpdater.newUpdater( + LockFreeObjectMap.class, + ObjectMap.class, + "hashMap"); + + private volatile ObjectMap hashMap; + + // synchronized is used here to ensure the "single writer principle", and make sure that ONLY one thread at a time can enter this + // section. Because of this, we can have unlimited reader threads all going at the same time, without contention (which is our + // use-case 99% of the time) + + /** + * Constructs an empty HashMap with the default initial capacity + * (16) and the default load factor (0.75). + */ + public + LockFreeObjectMap() { + hashMap = new ObjectMap(); + } + + /** + * Constructs an empty HashMap with the specified initial + * capacity and the default load factor (0.75). + * + * @param initialCapacity the initial capacity. + * + * @throws IllegalArgumentException if the initial capacity is negative. + */ + public + LockFreeObjectMap(int initialCapacity) { + hashMap = new ObjectMap(initialCapacity); + } + + /** + * Constructs an empty HashMap with the specified initial + * capacity and load factor. + * + * @param initialCapacity the initial capacity + * @param loadFactor the load factor + * + * @throws IllegalArgumentException if the initial capacity is negative + * or the load factor is nonpositive + */ + public + LockFreeObjectMap(int initialCapacity, float loadFactor) { + this.hashMap = new ObjectMap(initialCapacity, loadFactor); + } + + public + int size() { + // use the SWP to get a lock-free get of the value + return mapREF.get(this) + .size; + } + + public + boolean isEmpty() { + // use the SWP to get a lock-free get of the value + return mapREF.get(this) + .size == 0; + } + + public + boolean containsKey(final K key) { + // use the SWP to get a lock-free get of the value + return mapREF.get(this) + .containsKey(key); + } + + public + boolean containsValue(final V value, boolean identity) { + // use the SWP to get a lock-free get of the value + return mapREF.get(this) + .containsValue(value, identity); + } + + @SuppressWarnings("unchecked") + public + V get(final K key) { + // use the SWP to get a lock-free get of the value + return (V) mapREF.get(this) + .get(key); + } + + public synchronized + V put(final K key, final V value) { + return hashMap.put(key, value); + } + + public synchronized + V remove(final K key) { + return hashMap.remove(key); + } + + public synchronized + void putAll(final ObjectMap map) { + this.hashMap.putAll(map); + } + + public synchronized + void clear() { + hashMap.clear(); + } + + /** + * DO NOT MODIFY THE MAP VIA THIS! It will result in unknown object visibility! + */ + public + Keys keySet() { + return mapREF.get(this).keys(); + } + + /** + * DO NOT MODIFY THE MAP VIA THIS! It will result in unknown object visibility! + */ + public + Values values() { + return mapREF.get(this).values(); + } + + /** + * DO NOT MODIFY THE MAP VIA THIS! It will result in unknown object visibility! + */ + public + Entries entrySet() { + return mapREF.get(this).entries(); + } + + /** + * Identity equals only! + */ + @Override + public + boolean equals(final Object o) { + return this == o; + } + + @Override + public + int hashCode() { + return mapREF.get(this) + .hashCode(); + } + + @Override + public + String toString() { + return mapREF.get(this) + .toString(); + } +} diff --git a/src/dorkbox/util/collections/LockFreeSet.java b/src/dorkbox/util/collections/LockFreeSet.java index 0c87ee1..319810a 100644 --- a/src/dorkbox/util/collections/LockFreeSet.java +++ b/src/dorkbox/util/collections/LockFreeSet.java @@ -15,11 +15,7 @@ */ package dorkbox.util.collections; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; +import java.util.*; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; /** @@ -196,4 +192,24 @@ class LockFreeSet implements Set, Cloneable, java.io.Serializable { void clear() { hashSet.clear(); } + + @Override + public + boolean equals(final Object o) { + return setREF.get(this).equals(o); + } + + @Override + public + int hashCode() { + return setREF.get(this) + .hashCode(); + } + + @Override + public + String toString() { + return setREF.get(this) + .toString(); + } } diff --git a/src/dorkbox/util/collections/ObjectIntMap.java b/src/dorkbox/util/collections/ObjectIntMap.java index cc9330d..3addcf8 100644 --- a/src/dorkbox/util/collections/ObjectIntMap.java +++ b/src/dorkbox/util/collections/ObjectIntMap.java @@ -43,31 +43,6 @@ public class ObjectIntMap { private int stashCapacity; private int pushIterations; - // public static - // void main(String[] args) { - // ObjectIntMap test = new ObjectIntMap(4); - // String one = "One"; - // String four = "Four"; - // - // test.put(one, 1); - // test.put("Two", 2); - // test.put("Three", 3); - // test.put(four, 4); - // test.put(four, 1); - // test.put(one, 13); - // - // ObjectIntMap test2 = new ObjectIntMap(2); - // test2.put(one, 11); - // test2.put(four, 44); - // test2.put("Five", 55); - // - // test2.putAll(test); - // - // - // System.out.println(test.toString()); - // System.out.println(test2.toString()); - // } - /** Creates a new map with an initial capacity of 32 and a load factor of 0.8. This map will hold 25 items before growing the * backing table. */ public ObjectIntMap () {