From eaa45976c7038e3effe2d817949430ec6fe7039d Mon Sep 17 00:00:00 2001 From: Speiger Date: Fri, 10 Dec 2021 10:55:16 +0100 Subject: [PATCH] Added over 6k tests... - Fixed: ImmutableMaps issues thanks to the tests. Roughly the same as the rest of the maps - Fixed: RB/AVLTreeMaps issues. Roughly the same as the rest of the maps --- Changelog.md | 15 ++- .../LinkedOpenCustomHashMap.template | 19 ++- .../customHash/OpenCustomHashMap.template | 69 ++++++++--- .../maps/impl/hash/LinkedOpenHashMap.template | 43 ++++++- .../maps/impl/hash/OpenHashMap.template | 15 ++- .../immutable/ImmutableOpenHashMap.template | 61 +++++++-- .../maps/impl/tree/AVLTreeMap.template | 116 +++++++++++++++-- .../maps/impl/tree/RBTreeMap.template | 117 ++++++++++++++++-- .../src/collections/objects/map/MapTest.java | 40 ------ .../objects/map/ObjectMapTests.java | 90 ++++++++++++++ 10 files changed, 484 insertions(+), 101 deletions(-) delete mode 100644 src/test/java/speiger/src/collections/objects/map/MapTest.java create mode 100644 src/test/java/speiger/src/collections/objects/map/ObjectMapTests.java diff --git a/Changelog.md b/Changelog.md index 0b70f398..f88516fc 100644 --- a/Changelog.md +++ b/Changelog.md @@ -18,11 +18,16 @@ - Fixed: Map.Entry toString wasn't writing values not like it should do. - Fixed: Set.hashCode now is the sum of the elements instead of a Unique HashCode based on the elements. - Fixed: Added missing NonNull Checks. -- Fixed: OpenHashMap.containsValue implementation was wrong. -- Fixed: OpenHashMap.compute/present/absent now works how it is specified in the Java Documentation -- Fixed: OpenHashMap.merge/BulkMerge now works how it is specified in the Java Documentation -- Fixed: OpenHashMap.keySet.remove was causing a infinite loop. -- Fixed: OpenHashMap.mapIterator now no longer crashes in certain cases. +- Fixed: Custom/OpenHashMap.containsValue implementation was wrong. +- Fixed: Custom/OpenHashMap.compute/present/absent now works how it is specified in the Java Documentation +- Fixed: Custom/OpenHashMap.merge/BulkMerge now works how it is specified in the Java Documentation +- Fixed: Custom/Linked/OpenHashMap.keySet.remove was causing a infinite loop. +- Fixed: Custom/Linked/OpenHashMap.entrySet.contains was not correctly comparing the entry. +- Fixed: Custom/OpenHashMap.mapIterator now no longer crashes in certain cases. +- Added: Custom/LinkedOpenHashMap now takes use of the improved Iterator it has for containsValue +- Fixed: CustomOpenHashMap.keySet.forEach was basically putting out keys even if they were present +- Fixed: ImmutableMaps issues thanks to the tests. Roughly the same as the rest of the maps +- Fixed: RB/AVLTreeMaps issues. Roughly the same as the rest of the maps ### Version 0.4.5 - Added: removeAll/retainAll(Collection c, Consumer r) which receives all the elements that got deleted from the collection diff --git a/src/builder/resources/speiger/assets/collections/templates/maps/impl/customHash/LinkedOpenCustomHashMap.template b/src/builder/resources/speiger/assets/collections/templates/maps/impl/customHash/LinkedOpenCustomHashMap.template index 5806b8d7..f4a45e3d 100644 --- a/src/builder/resources/speiger/assets/collections/templates/maps/impl/customHash/LinkedOpenCustomHashMap.template +++ b/src/builder/resources/speiger/assets/collections/templates/maps/impl/customHash/LinkedOpenCustomHashMap.template @@ -820,8 +820,19 @@ public class LINKED_CUSTOM_HASH_MAP KEY_VALUE_GENERIC_TYPE extends CUSTOM_HASH_M @Deprecated public boolean contains(Object o) { if(o instanceof Map.Entry) { - if(o instanceof MAP.Entry) return LINKED_CUSTOM_HASH_MAP.this.containsKey(((MAP.Entry KEY_VALUE_GENERIC_TYPE)o).ENTRY_KEY()); - return LINKED_CUSTOM_HASH_MAP.this.containsKey(((Map.Entry)o).getKey()); + if(o instanceof MAP.Entry) { + MAP.Entry KEY_VALUE_GENERIC_TYPE entry = (MAP.Entry KEY_VALUE_GENERIC_TYPE)o; + int index = LINKED_CUSTOM_HASH_MAP.this.findIndex(entry.ENTRY_KEY()); + if(index >= 0) return VALUE_EQUALS(entry.ENTRY_VALUE(), LINKED_CUSTOM_HASH_MAP.this.values[index]); + } + else { + Map.Entry entry = (Map.Entry)o; +#if !TYPE_OBJECT + if(!(entry.getKey() instanceof CLASS_TYPE)) return false; +#endif + int index = LINKED_CUSTOM_HASH_MAP.this.findIndex((CLASS_TYPE)entry.getKey()); + if(index >= 0) return Objects.equals(entry.getValue(), VALUE_TO_OBJ(LINKED_CUSTOM_HASH_MAP.this.values[index])); + } } return false; } @@ -873,7 +884,7 @@ public class LINKED_CUSTOM_HASH_MAP KEY_VALUE_GENERIC_TYPE extends CUSTOM_HASH_M @Override public boolean remove(Object o) { int oldSize = size; - remove(o); + LINKED_CUSTOM_HASH_MAP.this.remove(o); return size != oldSize; } @@ -886,7 +897,7 @@ public class LINKED_CUSTOM_HASH_MAP KEY_VALUE_GENERIC_TYPE extends CUSTOM_HASH_M @Override public boolean remove(KEY_TYPE o) { int oldSize = size; - remove(o); + LINKED_CUSTOM_HASH_MAP.this.remove(o); return size != oldSize; } diff --git a/src/builder/resources/speiger/assets/collections/templates/maps/impl/customHash/OpenCustomHashMap.template b/src/builder/resources/speiger/assets/collections/templates/maps/impl/customHash/OpenCustomHashMap.template index 805019b4..591519dc 100644 --- a/src/builder/resources/speiger/assets/collections/templates/maps/impl/customHash/OpenCustomHashMap.template +++ b/src/builder/resources/speiger/assets/collections/templates/maps/impl/customHash/OpenCustomHashMap.template @@ -307,8 +307,7 @@ public class CUSTOM_HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VAL #if !VALUE_OBJECT @Override public boolean containsValue(VALUE_TYPE value) { - if(VALUE_EQUALS(value, values[nullIndex])) return true; - for(int i = nullIndex-1;i >= 0;i--) + for(int i = nullIndex;i >= 0;i--) if(!strategy.equals(keys[i], EMPTY_KEY_VALUE) && VALUE_EQUALS(values[i], value)) return true; return false; } @@ -317,9 +316,12 @@ public class CUSTOM_HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VAL @Override @ValuePrimitive public boolean containsValue(Object value) { - if((value == null && VALUE_EQUALS(values[nullIndex], getDefaultReturnValue())) || EQUALS_VALUE_TYPE(values[nullIndex], value)) return true; - for(int i = nullIndex-1;i >= 0;i--) + for(int i = nullIndex;i >= 0;i--) +#if VALUE_OBJECT + if(!strategy.equals(keys[i], EMPTY_KEY_VALUE) && EQUALS_VALUE_TYPE(values[i], value)) return true; +#else if(!strategy.equals(keys[i], EMPTY_KEY_VALUE) && ((value == null && values[i] == getDefaultReturnValue()) || EQUALS_VALUE_TYPE(values[i], value))) return true; +#endif return false; } @@ -338,7 +340,6 @@ public class CUSTOM_HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VAL } @Override - @Deprecated public CLASS_VALUE_TYPE remove(Object key) { #if !TYPE_OBJECT if(!(key instanceof CLASS_TYPE)) return getDefaultReturnValue(); @@ -495,6 +496,7 @@ public class CUSTOM_HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VAL @Override public VALUE_TYPE COMPUTE(KEY_TYPE key, UNARY_OPERATOR KEY_VALUE_GENERIC_TYPE mappingFunction) { + Objects.requireNonNull(mappingFunction); int index = findIndex(key); if(index < 0) { VALUE_TYPE newValue = mappingFunction.APPLY_VALUE(key, getDefaultReturnValue()); @@ -513,6 +515,7 @@ public class CUSTOM_HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VAL @Override public VALUE_TYPE COMPUTE_IF_ABSENT(KEY_TYPE key, FUNCTION KEY_VALUE_GENERIC_TYPE mappingFunction) { + Objects.requireNonNull(mappingFunction); int index = findIndex(key); if(index < 0) { VALUE_TYPE newValue = mappingFunction.GET_VALUE(key); @@ -520,11 +523,18 @@ public class CUSTOM_HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VAL insert(-index-1, key, newValue); return newValue; } - return values[index]; + VALUE_TYPE newValue = values[index]; + if(VALUE_EQUALS(newValue, getDefaultReturnValue())) { + newValue = mappingFunction.GET_VALUE(key); + if(VALUE_EQUALS(newValue, getDefaultReturnValue())) return newValue; + values[index] = newValue; + } + return newValue; } @Override public VALUE_TYPE SUPPLY_IF_ABSENT(KEY_TYPE key, VALUE_SUPPLIER VALUE_GENERIC_TYPE valueProvider) { + Objects.requireNonNull(valueProvider); int index = findIndex(key); if(index < 0) { VALUE_TYPE newValue = valueProvider.VALUE_GET_KEY(); @@ -532,13 +542,20 @@ public class CUSTOM_HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VAL insert(-index-1, key, newValue); return newValue; } - return values[index]; + VALUE_TYPE newValue = values[index]; + if(VALUE_EQUALS(newValue, getDefaultReturnValue())) { + newValue = valueProvider.VALUE_GET_KEY(); + if(VALUE_EQUALS(newValue, getDefaultReturnValue())) return newValue; + values[index] = newValue; + } + return newValue; } @Override public VALUE_TYPE COMPUTE_IF_PRESENT(KEY_TYPE key, UNARY_OPERATOR KEY_VALUE_GENERIC_TYPE mappingFunction) { + Objects.requireNonNull(mappingFunction); int index = findIndex(key); - if(index < 0) return getDefaultReturnValue(); + if(index < 0 || VALUE_EQUALS(values[index], getDefaultReturnValue())) return getDefaultReturnValue(); VALUE_TYPE newValue = mappingFunction.APPLY_VALUE(key, values[index]); if(VALUE_EQUALS(newValue, getDefaultReturnValue())) { removeIndex(index); @@ -550,8 +567,12 @@ public class CUSTOM_HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VAL @Override public VALUE_TYPE MERGE(KEY_TYPE key, VALUE_TYPE value, VALUE_UNARY_OPERATOR VALUE_VALUE_GENERIC_TYPE mappingFunction) { + Objects.requireNonNull(mappingFunction); +#if VALUE_OBJECT + Objects.requireNonNull(value); +#endif int index = findIndex(key); - VALUE_TYPE newValue = index < 0 ? value : mappingFunction.APPLY_VALUE(values[index], value); + VALUE_TYPE newValue = index < 0 || VALUE_EQUALS(values[index], getDefaultReturnValue()) ? value : mappingFunction.APPLY_VALUE(values[index], value); if(VALUE_EQUALS(newValue, getDefaultReturnValue())) { if(index >= 0) removeIndex(index); @@ -567,7 +588,7 @@ public class CUSTOM_HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VAL for(MAP.Entry KEY_VALUE_GENERIC_TYPE entry : MAPS.fastIterable(m)) { KEY_TYPE key = entry.ENTRY_KEY(); int index = findIndex(key); - VALUE_TYPE newValue = index < 0 ? entry.ENTRY_VALUE() : mappingFunction.APPLY_VALUE(values[index], entry.ENTRY_VALUE()); + VALUE_TYPE newValue = index < 0 || VALUE_EQUALS(values[index], getDefaultReturnValue()) ? entry.ENTRY_VALUE() : mappingFunction.APPLY_VALUE(values[index], entry.ENTRY_VALUE()); if(VALUE_EQUALS(newValue, getDefaultReturnValue())) { if(index >= 0) removeIndex(index); @@ -784,7 +805,7 @@ public class CUSTOM_HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VAL @Override public String toString() { - return KEY_TO_STRING(keys[index]) + "->" + VALUE_TO_STRING(values[index]); + return KEY_TO_STRING(keys[index]) + "=" + VALUE_TO_STRING(values[index]); } } @@ -968,8 +989,19 @@ public class CUSTOM_HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VAL @Override public boolean contains(Object o) { if(o instanceof Map.Entry) { - if(o instanceof MAP.Entry) return CUSTOM_HASH_MAP.this.containsKey(((MAP.Entry KEY_VALUE_GENERIC_TYPE)o).ENTRY_KEY()); - return CUSTOM_HASH_MAP.this.containsKey(((Map.Entry)o).getKey()); + if(o instanceof MAP.Entry) { + MAP.Entry KEY_VALUE_GENERIC_TYPE entry = (MAP.Entry KEY_VALUE_GENERIC_TYPE)o; + int index = CUSTOM_HASH_MAP.this.findIndex(entry.ENTRY_KEY()); + if(index >= 0) return VALUE_EQUALS(entry.ENTRY_VALUE(), CUSTOM_HASH_MAP.this.values[index]); + } + else { + Map.Entry entry = (Map.Entry)o; +#if !TYPE_OBJECT + if(!(entry.getKey() instanceof CLASS_TYPE)) return false; +#endif + int index = CUSTOM_HASH_MAP.this.findIndex((CLASS_TYPE)entry.getKey()); + if(index >= 0) return Objects.equals(entry.getValue(), VALUE_TO_OBJ(CUSTOM_HASH_MAP.this.values[index])); + } } return false; } @@ -998,7 +1030,7 @@ public class CUSTOM_HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VAL @Override public boolean remove(Object o) { int oldSize = size; - remove(o); + CUSTOM_HASH_MAP.this.remove(o); return size != oldSize; } @@ -1011,7 +1043,7 @@ public class CUSTOM_HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VAL @Override public boolean remove(KEY_TYPE o) { int oldSize = size; - remove(o); + CUSTOM_HASH_MAP.this.remove(o); return size != oldSize; } @@ -1041,9 +1073,10 @@ public class CUSTOM_HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VAL @Override public void forEach(CONSUMER KEY_SUPER_GENERIC_TYPE action) { + Objects.requireNonNull(action); if(containsNull) action.accept(keys[nullIndex]); for(int i = nullIndex-1;i>=0;i--) - if(strategy.equals(keys[i], EMPTY_KEY_VALUE)) action.accept(keys[i]); + if(!strategy.equals(keys[i], EMPTY_KEY_VALUE)) action.accept(keys[i]); } @Override @@ -1406,9 +1439,9 @@ public class CUSTOM_HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VAL keys[nullIndex] = EMPTY_KEY_VALUE; values[nullIndex] = EMPTY_VALUE; } - else if(pos >= 0) shiftKeys(pos); + else if(lastReturned >= 0) shiftKeys(lastReturned); else { - CUSTOM_HASH_MAP.this.remove(wrapped.GET_KEY(-pos - 1)); + CUSTOM_HASH_MAP.this.remove(wrapped.GET_KEY(-lastReturned - 1)); return; } size--; diff --git a/src/builder/resources/speiger/assets/collections/templates/maps/impl/hash/LinkedOpenHashMap.template b/src/builder/resources/speiger/assets/collections/templates/maps/impl/hash/LinkedOpenHashMap.template index 3310c5fc..9b5b19de 100644 --- a/src/builder/resources/speiger/assets/collections/templates/maps/impl/hash/LinkedOpenHashMap.template +++ b/src/builder/resources/speiger/assets/collections/templates/maps/impl/hash/LinkedOpenHashMap.template @@ -323,6 +323,33 @@ public class LINKED_HASH_MAP KEY_VALUE_GENERIC_TYPE extends HASH_MAP KEY_VALUE_G return values[index]; } +#if !VALUE_OBJECT + @Override + public boolean containsValue(VALUE_TYPE value) { + int index = firstIndex; + while(index != -1) { + if(VALUE_EQUALS(values[index], value)) return true; + index = (int)links[index]; + } + return false; + } + +#endif + @Override + @ValuePrimitive + public boolean containsValue(Object value) { + int index = firstIndex; + while(index != -1) { +#if VALUE_OBJECT + if(VALUE_EQUALS(values[index], value)) return true; +#else + if((value == null && values[index] == getDefaultReturnValue()) || EQUALS_VALUE_TYPE(values[index], value)) return true; +#endif + index = (int)links[index]; + } + return false; + } + @Override public LINKED_HASH_MAP KEY_VALUE_GENERIC_TYPE copy() { LINKED_HASH_MAP KEY_VALUE_GENERIC_TYPE map = new LINKED_HASH_MAPKV_BRACES(0, loadFactor); @@ -797,8 +824,16 @@ public class LINKED_HASH_MAP KEY_VALUE_GENERIC_TYPE extends HASH_MAP KEY_VALUE_G @Deprecated public boolean contains(Object o) { if(o instanceof Map.Entry) { - if(o instanceof MAP.Entry) return LINKED_HASH_MAP.this.containsKey(((MAP.Entry KEY_VALUE_GENERIC_TYPE)o).ENTRY_KEY()); - return LINKED_HASH_MAP.this.containsKey(((Map.Entry)o).getKey()); + if(o instanceof MAP.Entry) { + MAP.Entry KEY_VALUE_GENERIC_TYPE entry = (MAP.Entry KEY_VALUE_GENERIC_TYPE)o; + int index = LINKED_HASH_MAP.this.findIndex(entry.ENTRY_KEY()); + if(index >= 0) return VALUE_EQUALS(entry.ENTRY_VALUE(), LINKED_HASH_MAP.this.values[index]); + } + else { + Map.Entry entry = (Map.Entry)o; + int index = LINKED_HASH_MAP.this.findIndex(entry.getKey()); + if(index >= 0) return Objects.equals(entry.getValue(), VALUE_TO_OBJ(LINKED_HASH_MAP.this.values[index])); + } } return false; } @@ -850,7 +885,7 @@ public class LINKED_HASH_MAP KEY_VALUE_GENERIC_TYPE extends HASH_MAP KEY_VALUE_G @Override public boolean remove(Object o) { int oldSize = size; - remove(o); + LINKED_HASH_MAP.this.remove(o); return size != oldSize; } @@ -863,7 +898,7 @@ public class LINKED_HASH_MAP KEY_VALUE_GENERIC_TYPE extends HASH_MAP KEY_VALUE_G @Override public boolean remove(KEY_TYPE o) { int oldSize = size; - remove(o); + LINKED_HASH_MAP.this.remove(o); return size != oldSize; } diff --git a/src/builder/resources/speiger/assets/collections/templates/maps/impl/hash/OpenHashMap.template b/src/builder/resources/speiger/assets/collections/templates/maps/impl/hash/OpenHashMap.template index 2c078c77..65098fd4 100644 --- a/src/builder/resources/speiger/assets/collections/templates/maps/impl/hash/OpenHashMap.template +++ b/src/builder/resources/speiger/assets/collections/templates/maps/impl/hash/OpenHashMap.template @@ -288,7 +288,7 @@ public class HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_GENE @ValuePrimitive public boolean containsValue(Object value) { for(int i = nullIndex;i >= 0;i--) -#if TYPE_OBJECT +#if VALUE_OBJECT if(KEY_EQUALS_NOT_NULL(keys[i]) && EQUALS_VALUE_TYPE(values[i], value)) return true; #else if(KEY_EQUALS_NOT_NULL(keys[i]) && ((value == null && values[i] == getDefaultReturnValue()) || EQUALS_VALUE_TYPE(values[i], value))) return true; @@ -311,7 +311,6 @@ public class HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_GENE } @Override - @Deprecated public CLASS_VALUE_TYPE remove(Object key) { int slot = findIndex(key); if(slot < 0) return VALUE_TO_OBJ(getDefaultReturnValue()); @@ -458,6 +457,7 @@ public class HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_GENE @Override public VALUE_TYPE COMPUTE(KEY_TYPE key, UNARY_OPERATOR KEY_VALUE_GENERIC_TYPE mappingFunction) { + Objects.requireNonNull(mappingFunction); int index = findIndex(key); if(index < 0) { VALUE_TYPE newValue = mappingFunction.APPLY_VALUE(key, getDefaultReturnValue()); @@ -476,6 +476,7 @@ public class HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_GENE @Override public VALUE_TYPE COMPUTE_IF_ABSENT(KEY_TYPE key, FUNCTION KEY_VALUE_GENERIC_TYPE mappingFunction) { + Objects.requireNonNull(mappingFunction); int index = findIndex(key); if(index < 0) { VALUE_TYPE newValue = mappingFunction.GET_VALUE(key); @@ -494,6 +495,7 @@ public class HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_GENE @Override public VALUE_TYPE SUPPLY_IF_ABSENT(KEY_TYPE key, VALUE_SUPPLIER VALUE_GENERIC_TYPE valueProvider) { + Objects.requireNonNull(valueProvider); int index = findIndex(key); if(index < 0) { VALUE_TYPE newValue = valueProvider.VALUE_GET_KEY(); @@ -501,11 +503,18 @@ public class HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_GENE insert(-index-1, key, newValue); return newValue; } - return values[index]; + VALUE_TYPE newValue = values[index]; + if(VALUE_EQUALS(newValue, getDefaultReturnValue())) { + newValue = valueProvider.VALUE_GET_KEY(); + if(VALUE_EQUALS(newValue, getDefaultReturnValue())) return newValue; + values[index] = newValue; + } + return newValue; } @Override public VALUE_TYPE COMPUTE_IF_PRESENT(KEY_TYPE key, UNARY_OPERATOR KEY_VALUE_GENERIC_TYPE mappingFunction) { + Objects.requireNonNull(mappingFunction); int index = findIndex(key); if(index < 0 || VALUE_EQUALS(values[index], getDefaultReturnValue())) return getDefaultReturnValue(); VALUE_TYPE newValue = mappingFunction.APPLY_VALUE(key, values[index]); diff --git a/src/builder/resources/speiger/assets/collections/templates/maps/impl/immutable/ImmutableOpenHashMap.template b/src/builder/resources/speiger/assets/collections/templates/maps/impl/immutable/ImmutableOpenHashMap.template index 5c7aa6c6..0139838c 100644 --- a/src/builder/resources/speiger/assets/collections/templates/maps/impl/immutable/ImmutableOpenHashMap.template +++ b/src/builder/resources/speiger/assets/collections/templates/maps/impl/immutable/ImmutableOpenHashMap.template @@ -237,18 +237,34 @@ public class IMMUTABLE_HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_ { KEY_TYPE o = a[i]; if(KEY_EQUALS_NULL(o)) { - if(!containsNull) size++; + if(!containsNull) { + size++; + if(prev != -1) { + newLinks[prev] ^= ((newLinks[prev] ^ (newSize & 0xFFFFFFFFL)) & 0xFFFFFFFFL); + newLinks[newSize] ^= ((newLinks[newSize] ^ ((prev & 0xFFFFFFFFL) << 32)) & 0xFFFFFFFF00000000L); + prev = newSize; + } + else { + prev = firstIndex = newSize; + newLinks[newSize] = -1L; + } + } containsNull = true; + newValues[newSize] = b[i]; continue; } boolean found = true; int pos = HashUtil.mix(KEY_TO_HASH(o)) & newMask; KEY_TYPE current = newKeys[pos]; if(KEY_EQUALS_NOT_NULL(current)) { - if(KEY_EQUALS(current, o)) continue; + if(KEY_EQUALS(current, o)) { + newValues[pos] = b[i]; + continue; + } while(KEY_EQUALS_NOT_NULL((current = newKeys[pos = (++pos & mask)]))) { if(KEY_EQUALS(current, o)) { found = false; + newValues[pos] = b[i]; break; } } @@ -256,7 +272,7 @@ public class IMMUTABLE_HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_ if(found) { size++; newKeys[pos] = o; - values[pos] = b[i]; + newValues[pos] = b[i]; if(prev != -1) { newLinks[prev] ^= ((newLinks[prev] ^ (pos & 0xFFFFFFFFL)) & 0xFFFFFFFFL); newLinks[pos] ^= ((newLinks[pos] ^ ((prev & 0xFFFFFFFFL) << 32)) & 0xFFFFFFFF00000000L); @@ -268,7 +284,7 @@ public class IMMUTABLE_HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_ } } } - nullIndex = size; + nullIndex = newSize; mask = newMask; keys = newKeys; values = newValues; @@ -316,9 +332,11 @@ public class IMMUTABLE_HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_ #if !VALUE_OBJECT @Override public boolean containsValue(VALUE_TYPE value) { - if(VALUE_EQUALS(value, values[nullIndex])) return true; - for(int i = nullIndex-1;i >= 0;i--) - if(KEY_EQUALS_NOT_NULL(keys[i]) && VALUE_EQUALS(values[i], value)) return true; + int index = firstIndex; + while(index != -1) { + if(VALUE_EQUALS(values[index], value)) return true; + index = (int)links[index]; + } return false; } @@ -326,9 +344,15 @@ public class IMMUTABLE_HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_ @Override @ValuePrimitive public boolean containsValue(Object value) { - if((value == null && VALUE_EQUALS(values[nullIndex], getDefaultReturnValue())) || EQUALS_VALUE_TYPE(values[nullIndex], value)) return true; - for(int i = nullIndex-1;i >= 0;i--) - if(KEY_EQUALS_NOT_NULL(keys[i]) && ((value == null && values[i] == getDefaultReturnValue()) || EQUALS_VALUE_TYPE(values[i], value))) return true; + int index = firstIndex; + while(index != -1) { +#if VALUE_OBJECT + if(VALUE_EQUALS(values[index], value)) return true; +#else + if((value == null && values[index] == getDefaultReturnValue()) || EQUALS_VALUE_TYPE(values[index], value)) return true; +#endif + index = (int)links[index]; + } return false; } @@ -544,6 +568,9 @@ public class IMMUTABLE_HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_ } Map.Entry entry = (Map.Entry)obj; Object key = entry.getKey(); +#if !TYPE_OBJECT + if(!(key instanceof CLASS_TYPE)) return false; +#endif Object value = entry.getValue(); #if TYPE_OBJECT && VALUE_OBJECT return KEY_EQUALS(keys[index], key) && VALUE_EQUALS(values[index], value); @@ -565,7 +592,7 @@ public class IMMUTABLE_HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_ @Override public String toString() { - return KEY_TO_STRING(keys[index]) + "->" + VALUE_TO_STRING(values[index]); + return KEY_TO_STRING(keys[index]) + "=" + VALUE_TO_STRING(values[index]); } } @@ -757,8 +784,16 @@ public class IMMUTABLE_HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_ @Deprecated public boolean contains(Object o) { if(o instanceof Map.Entry) { - if(o instanceof MAP.Entry) return IMMUTABLE_HASH_MAP.this.containsKey(((MAP.Entry KEY_VALUE_GENERIC_TYPE)o).ENTRY_KEY()); - return IMMUTABLE_HASH_MAP.this.containsKey(((Map.Entry)o).getKey()); + if(o instanceof MAP.Entry) { + MAP.Entry KEY_VALUE_GENERIC_TYPE entry = (MAP.Entry KEY_VALUE_GENERIC_TYPE)o; + int index = IMMUTABLE_HASH_MAP.this.findIndex(entry.ENTRY_KEY()); + if(index >= 0) return VALUE_EQUALS(entry.ENTRY_VALUE(), IMMUTABLE_HASH_MAP.this.values[index]); + } + else { + Map.Entry entry = (Map.Entry)o; + int index = IMMUTABLE_HASH_MAP.this.findIndex(entry.getKey()); + if(index >= 0) return Objects.equals(entry.getValue(), VALUE_TO_OBJ(IMMUTABLE_HASH_MAP.this.values[index])); + } } return false; } diff --git a/src/builder/resources/speiger/assets/collections/templates/maps/impl/tree/AVLTreeMap.template b/src/builder/resources/speiger/assets/collections/templates/maps/impl/tree/AVLTreeMap.template index da0cfbd7..d5617823 100644 --- a/src/builder/resources/speiger/assets/collections/templates/maps/impl/tree/AVLTreeMap.template +++ b/src/builder/resources/speiger/assets/collections/templates/maps/impl/tree/AVLTreeMap.template @@ -233,6 +233,9 @@ public class AVL_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_ #endif @Override public VALUE_TYPE put(KEY_TYPE key, VALUE_TYPE value) { +#if TYPE_OBJECT + Objects.requireNonNull(key); +#endif if(tree == null) { tree = first = last = new EntryKV_BRACES(key, value, null); size++; @@ -267,6 +270,9 @@ public class AVL_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_ @Override public VALUE_TYPE putIfAbsent(KEY_TYPE key, VALUE_TYPE value) { +#if TYPE_OBJECT + Objects.requireNonNull(key); +#endif if(tree == null) { tree = first = last = new EntryKV_BRACES(key, value, null); size++; @@ -302,6 +308,9 @@ public class AVL_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_ #if VALUE_PRIMITIVES @Override public VALUE_TYPE addTo(KEY_TYPE key, VALUE_TYPE value) { +#if TYPE_OBJECT + Objects.requireNonNull(key); +#endif if(tree == null) { tree = first = last = new EntryKV_BRACES(key, value, null); size++; @@ -510,6 +519,10 @@ public class AVL_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_ @Override public VALUE_TYPE COMPUTE(KEY_TYPE key, UNARY_OPERATOR KEY_VALUE_GENERIC_TYPE mappingFunction) { + Objects.requireNonNull(mappingFunction); +#if TYPE_OBJECT + Objects.requireNonNull(key); +#endif Entry KEY_VALUE_GENERIC_TYPE entry = findNode(key); if(entry == null) { VALUE_TYPE newValue = mappingFunction.APPLY_VALUE(key, getDefaultReturnValue()); @@ -528,6 +541,10 @@ public class AVL_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_ @Override public VALUE_TYPE COMPUTE_IF_ABSENT(KEY_TYPE key, FUNCTION KEY_VALUE_GENERIC_TYPE mappingFunction) { + Objects.requireNonNull(mappingFunction); +#if TYPE_OBJECT + Objects.requireNonNull(key); +#endif Entry KEY_VALUE_GENERIC_TYPE entry = findNode(key); if(entry == null) { VALUE_TYPE newValue = mappingFunction.GET_VALUE(key); @@ -535,11 +552,20 @@ public class AVL_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_ put(key, newValue); return newValue; } + if(VALUE_EQUALS(entry.value, getDefaultReturnValue())) { + VALUE_TYPE newValue = mappingFunction.GET_VALUE(key); + if(VALUE_EQUALS(newValue, getDefaultReturnValue())) return newValue; + entry.value = newValue; + } return entry.value; } @Override public VALUE_TYPE SUPPLY_IF_ABSENT(KEY_TYPE key, VALUE_SUPPLIER VALUE_GENERIC_TYPE valueProvider) { + Objects.requireNonNull(valueProvider); +#if TYPE_OBJECT + Objects.requireNonNull(key); +#endif Entry KEY_VALUE_GENERIC_TYPE entry = findNode(key); if(entry == null) { VALUE_TYPE newValue = valueProvider.VALUE_GET_KEY(); @@ -547,13 +573,22 @@ public class AVL_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_ put(key, newValue); return newValue; } + if(VALUE_EQUALS(entry.value, getDefaultReturnValue())) { + VALUE_TYPE newValue = valueProvider.VALUE_GET_KEY(); + if(VALUE_EQUALS(newValue, getDefaultReturnValue())) return newValue; + entry.value = newValue; + } return entry.value; } @Override public VALUE_TYPE COMPUTE_IF_PRESENT(KEY_TYPE key, UNARY_OPERATOR KEY_VALUE_GENERIC_TYPE mappingFunction) { + Objects.requireNonNull(mappingFunction); +#if TYPE_OBJECT + Objects.requireNonNull(key); +#endif Entry KEY_VALUE_GENERIC_TYPE entry = findNode(key); - if(entry == null) return getDefaultReturnValue(); + if(entry == null || VALUE_EQUALS(entry.value, getDefaultReturnValue())) return getDefaultReturnValue(); VALUE_TYPE newValue = mappingFunction.APPLY_VALUE(key, entry.value); if(VALUE_EQUALS(newValue, getDefaultReturnValue())) { removeNode(entry); @@ -565,8 +600,12 @@ public class AVL_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_ @Override public VALUE_TYPE MERGE(KEY_TYPE key, VALUE_TYPE value, VALUE_UNARY_OPERATOR VALUE_VALUE_GENERIC_TYPE mappingFunction) { + Objects.requireNonNull(mappingFunction); +#if TYPE_OBJECT + Objects.requireNonNull(key); +#endif Entry KEY_VALUE_GENERIC_TYPE entry = findNode(key); - VALUE_TYPE newValue = entry == null ? value : mappingFunction.APPLY_VALUE(entry.value, value); + VALUE_TYPE newValue = entry == null || VALUE_EQUALS(entry.value, getDefaultReturnValue()) ? value : mappingFunction.APPLY_VALUE(entry.value, value); if(VALUE_EQUALS(newValue, getDefaultReturnValue())) { if(entry != null) removeNode(entry); @@ -582,7 +621,7 @@ public class AVL_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_ for(MAP.Entry KEY_VALUE_GENERIC_TYPE entry : MAPS.fastIterable(m)) { KEY_TYPE key = entry.ENTRY_KEY(); Entry KEY_VALUE_GENERIC_TYPE subEntry = findNode(key); - VALUE_TYPE newValue = subEntry == null ? entry.ENTRY_VALUE() : mappingFunction.APPLY_VALUE(subEntry.value, entry.ENTRY_VALUE()); + VALUE_TYPE newValue = subEntry == null || VALUE_EQUALS(subEntry.value, getDefaultReturnValue()) ? entry.ENTRY_VALUE() : mappingFunction.APPLY_VALUE(subEntry.value, entry.ENTRY_VALUE()); if(VALUE_EQUALS(newValue, getDefaultReturnValue())) { if(subEntry != null) removeNode(subEntry); @@ -1491,8 +1530,21 @@ public class AVL_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_ @Deprecated public boolean contains(Object o) { if(o instanceof Map.Entry) { - if(o instanceof MAP.Entry) return NavigableSubMap.this.containsKey(((MAP.Entry KEY_VALUE_GENERIC_TYPE)o).ENTRY_KEY()); - return NavigableSubMap.this.containsKey(((Map.Entry)o).getKey()); + if(o instanceof MAP.Entry) { + MAP.Entry KEY_VALUE_GENERIC_TYPE entry = (MAP.Entry KEY_VALUE_GENERIC_TYPE)o; + if(entry.getKey() == null) return false; + AVL_TREE_MAP.Entry KEY_VALUE_GENERIC_TYPE subEntry = m.findNode(entry.ENTRY_KEY()); + if(subEntry != null) return VALUE_EQUALS(entry.ENTRY_VALUE(), subEntry.value); + } + else { + Map.Entry entry = (Map.Entry)o; + if(entry.getKey() == null) return false; +#if !TYPE_OBJECT + if(!(entry.getKey() instanceof CLASS_TYPE)) return false; +#endif + AVL_TREE_MAP.Entry KEY_VALUE_GENERIC_TYPE subEntry = m.findNode((CLASS_TYPE)entry.getKey()); + if(subEntry != null) return Objects.equals(entry.getValue(), VALUE_TO_OBJ(subEntry.value)); + } } return false; } @@ -1884,8 +1936,21 @@ public class AVL_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_ @Deprecated public boolean contains(Object o) { if(o instanceof Map.Entry) { - if(o instanceof MAP.Entry) return AVL_TREE_MAP.this.containsKey(((MAP.Entry KEY_VALUE_GENERIC_TYPE)o).ENTRY_KEY()); - return AVL_TREE_MAP.this.containsKey(((Map.Entry)o).getKey()); + if(o instanceof MAP.Entry) { + MAP.Entry KEY_VALUE_GENERIC_TYPE entry = (MAP.Entry KEY_VALUE_GENERIC_TYPE)o; + if(entry.getKey() == null) return false; + AVL_TREE_MAP.Entry KEY_VALUE_GENERIC_TYPE subEntry = AVL_TREE_MAP.this.findNode(entry.ENTRY_KEY()); + if(subEntry != null) return VALUE_EQUALS(entry.ENTRY_VALUE(), subEntry.value); + } + else { + Map.Entry entry = (Map.Entry)o; + if(entry.getKey() == null) return false; +#if !TYPE_OBJECT + if(!(entry.getKey() instanceof CLASS_TYPE)) return false; +#endif + AVL_TREE_MAP.Entry KEY_VALUE_GENERIC_TYPE subEntry = AVL_TREE_MAP.this.findNode((CLASS_TYPE)entry.getKey()); + if(subEntry != null) return Objects.equals(entry.getValue(), VALUE_TO_OBJ(subEntry.value)); + } } return false; } @@ -2551,6 +2616,43 @@ public class AVL_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_ } #endif + @Override + public boolean equals(Object obj) { + if(obj instanceof Map.Entry) { + if(obj instanceof MAP.Entry) { + MAP.Entry KEY_VALUE_GENERIC_TYPE entry = (MAP.Entry KEY_VALUE_GENERIC_TYPE)obj; +#if TYPE_OBJECT + if(entry.ENTRY_KEY() == null) return false; +#endif + return KEY_EQUALS(key, entry.ENTRY_KEY()) && VALUE_EQUALS(value, entry.ENTRY_VALUE()); + } + Map.Entry entry = (Map.Entry)obj; + Object otherKey = entry.getKey(); + if(otherKey == null) return false; + Object otherValue = entry.getValue(); +#if TYPE_OBJECT && VALUE_OBJECT + return KEY_EQUALS(key, otherKey) && VALUE_EQUALS(value, otherValue); +#else if TYPE_OBJECT + return otherValue instanceof CLASS_VALUE_TYPE && KEY_EQUALS(key, otherKey) && VALUE_EQUALS(value, CLASS_TO_VALUE(otherValue)); +#else if VALUE_OBJECT + return otherKey instanceof CLASS_TYPE && KEY_EQUALS(key, CLASS_TO_KEY(otherKey)) && VALUE_EQUALS(value, otherValue); +#else + return otherKey instanceof CLASS_TYPE && otherValue instanceof CLASS_VALUE_TYPE && KEY_EQUALS(key, CLASS_TO_KEY(otherKey)) && VALUE_EQUALS(value, CLASS_TO_VALUE(otherValue)); +#endif + } + return false; + } + + @Override + public int hashCode() { + return KEY_TO_HASH(key) ^ VALUE_TO_HASH(value); + } + + @Override + public String toString() { + return KEY_TO_STRING(key) + "=" + VALUE_TO_STRING(value); + } + int getHeight() { return state; } void updateHeight() { state = (1 + Math.max(left == null ? -1 : left.getHeight(), right == null ? -1 : right.getHeight())); } diff --git a/src/builder/resources/speiger/assets/collections/templates/maps/impl/tree/RBTreeMap.template b/src/builder/resources/speiger/assets/collections/templates/maps/impl/tree/RBTreeMap.template index dc38a8d9..763db3e3 100644 --- a/src/builder/resources/speiger/assets/collections/templates/maps/impl/tree/RBTreeMap.template +++ b/src/builder/resources/speiger/assets/collections/templates/maps/impl/tree/RBTreeMap.template @@ -233,6 +233,9 @@ public class RB_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_G #endif @Override public VALUE_TYPE put(KEY_TYPE key, VALUE_TYPE value) { +#if TYPE_OBJECT + Objects.requireNonNull(key); +#endif if(tree == null) { tree = first = last = new EntryKV_BRACES(key, value, null); size++; @@ -267,6 +270,10 @@ public class RB_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_G @Override public VALUE_TYPE putIfAbsent(KEY_TYPE key, VALUE_TYPE value) { +#if TYPE_OBJECT + Objects.requireNonNull(key); +#endif + if(tree == null) { tree = first = last = new EntryKV_BRACES(key, value, null); size++; @@ -302,6 +309,9 @@ public class RB_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_G #if VALUE_PRIMITIVES @Override public VALUE_TYPE addTo(KEY_TYPE key, VALUE_TYPE value) { +#if TYPE_OBJECT + Objects.requireNonNull(key); +#endif if(tree == null) { tree = first = last = new EntryKV_BRACES(key, value, null); size++; @@ -509,6 +519,10 @@ public class RB_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_G @Override public VALUE_TYPE COMPUTE(KEY_TYPE key, UNARY_OPERATOR KEY_VALUE_GENERIC_TYPE mappingFunction) { + Objects.requireNonNull(mappingFunction); +#if TYPE_OBJECT + Objects.requireNonNull(key); +#endif Entry KEY_VALUE_GENERIC_TYPE entry = findNode(key); if(entry == null) { VALUE_TYPE newValue = mappingFunction.APPLY_VALUE(key, getDefaultReturnValue()); @@ -527,6 +541,10 @@ public class RB_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_G @Override public VALUE_TYPE COMPUTE_IF_ABSENT(KEY_TYPE key, FUNCTION KEY_VALUE_GENERIC_TYPE mappingFunction) { + Objects.requireNonNull(mappingFunction); +#if TYPE_OBJECT + Objects.requireNonNull(key); +#endif Entry KEY_VALUE_GENERIC_TYPE entry = findNode(key); if(entry == null) { VALUE_TYPE newValue = mappingFunction.GET_VALUE(key); @@ -534,11 +552,20 @@ public class RB_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_G put(key, newValue); return newValue; } + if(VALUE_EQUALS(entry.value, getDefaultReturnValue())) { + VALUE_TYPE newValue = mappingFunction.GET_VALUE(key); + if(VALUE_EQUALS(newValue, getDefaultReturnValue())) return newValue; + entry.value = newValue; + } return entry.value; } @Override public VALUE_TYPE SUPPLY_IF_ABSENT(KEY_TYPE key, VALUE_SUPPLIER VALUE_GENERIC_TYPE valueProvider) { + Objects.requireNonNull(valueProvider); +#if TYPE_OBJECT + Objects.requireNonNull(key); +#endif Entry KEY_VALUE_GENERIC_TYPE entry = findNode(key); if(entry == null) { VALUE_TYPE newValue = valueProvider.VALUE_GET_KEY(); @@ -546,13 +573,22 @@ public class RB_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_G put(key, newValue); return newValue; } + if(VALUE_EQUALS(entry.value, getDefaultReturnValue())) { + VALUE_TYPE newValue = valueProvider.VALUE_GET_KEY(); + if(VALUE_EQUALS(newValue, getDefaultReturnValue())) return newValue; + entry.value = newValue; + } return entry.value; } @Override public VALUE_TYPE COMPUTE_IF_PRESENT(KEY_TYPE key, UNARY_OPERATOR KEY_VALUE_GENERIC_TYPE mappingFunction) { + Objects.requireNonNull(mappingFunction); +#if TYPE_OBJECT + Objects.requireNonNull(key); +#endif Entry KEY_VALUE_GENERIC_TYPE entry = findNode(key); - if(entry == null) return getDefaultReturnValue(); + if(entry == null || VALUE_EQUALS(entry.value, getDefaultReturnValue())) return getDefaultReturnValue(); VALUE_TYPE newValue = mappingFunction.APPLY_VALUE(key, entry.value); if(VALUE_EQUALS(newValue, getDefaultReturnValue())) { removeNode(entry); @@ -564,8 +600,12 @@ public class RB_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_G @Override public VALUE_TYPE MERGE(KEY_TYPE key, VALUE_TYPE value, VALUE_UNARY_OPERATOR VALUE_VALUE_GENERIC_TYPE mappingFunction) { + Objects.requireNonNull(mappingFunction); +#if TYPE_OBJECT + Objects.requireNonNull(key); +#endif Entry KEY_VALUE_GENERIC_TYPE entry = findNode(key); - VALUE_TYPE newValue = entry == null ? value : mappingFunction.APPLY_VALUE(entry.value, value); + VALUE_TYPE newValue = entry == null || VALUE_EQUALS(entry.value, getDefaultReturnValue()) ? value : mappingFunction.APPLY_VALUE(entry.value, value); if(VALUE_EQUALS(newValue, getDefaultReturnValue())) { if(entry != null) removeNode(entry); @@ -581,7 +621,7 @@ public class RB_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_G for(MAP.Entry KEY_VALUE_GENERIC_TYPE entry : MAPS.fastIterable(m)) { KEY_TYPE key = entry.ENTRY_KEY(); Entry KEY_VALUE_GENERIC_TYPE subEntry = findNode(key); - VALUE_TYPE newValue = subEntry == null ? entry.ENTRY_VALUE() : mappingFunction.APPLY_VALUE(subEntry.value, entry.ENTRY_VALUE()); + VALUE_TYPE newValue = subEntry == null || VALUE_EQUALS(subEntry.value, getDefaultReturnValue()) ? entry.ENTRY_VALUE() : mappingFunction.APPLY_VALUE(subEntry.value, entry.ENTRY_VALUE()); if(VALUE_EQUALS(newValue, getDefaultReturnValue())) { if(subEntry != null) removeNode(subEntry); @@ -1545,8 +1585,21 @@ public class RB_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_G @Deprecated public boolean contains(Object o) { if(o instanceof Map.Entry) { - if(o instanceof MAP.Entry) return NavigableSubMap.this.containsKey(((MAP.Entry KEY_VALUE_GENERIC_TYPE)o).ENTRY_KEY()); - return NavigableSubMap.this.containsKey(((Map.Entry)o).getKey()); + if(o instanceof MAP.Entry) { + MAP.Entry KEY_VALUE_GENERIC_TYPE entry = (MAP.Entry KEY_VALUE_GENERIC_TYPE)o; + if(entry.getKey() == null) return false; + RB_TREE_MAP.Entry KEY_VALUE_GENERIC_TYPE subEntry = m.findNode(entry.ENTRY_KEY()); + if(subEntry != null) return VALUE_EQUALS(entry.ENTRY_VALUE(), subEntry.value); + } + else { + Map.Entry entry = (Map.Entry)o; + if(entry.getKey() == null) return false; +#if !TYPE_OBJECT + if(!(entry.getKey() instanceof CLASS_TYPE)) return false; +#endif + RB_TREE_MAP.Entry KEY_VALUE_GENERIC_TYPE subEntry = m.findNode((CLASS_TYPE)entry.getKey()); + if(subEntry != null) return Objects.equals(entry.getValue(), VALUE_TO_OBJ(subEntry.value)); + } } return false; } @@ -1938,8 +1991,21 @@ public class RB_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_G @Deprecated public boolean contains(Object o) { if(o instanceof Map.Entry) { - if(o instanceof MAP.Entry) return RB_TREE_MAP.this.containsKey(((MAP.Entry KEY_VALUE_GENERIC_TYPE)o).ENTRY_KEY()); - return RB_TREE_MAP.this.containsKey(((Map.Entry)o).getKey()); + if(o instanceof MAP.Entry) { + MAP.Entry KEY_VALUE_GENERIC_TYPE entry = (MAP.Entry KEY_VALUE_GENERIC_TYPE)o; + if(entry.getKey() == null) return false; + RB_TREE_MAP.Entry KEY_VALUE_GENERIC_TYPE subEntry = RB_TREE_MAP.this.findNode(entry.ENTRY_KEY()); + if(subEntry != null) return VALUE_EQUALS(entry.ENTRY_VALUE(), subEntry.value); + } + else { + Map.Entry entry = (Map.Entry)o; + if(entry.getKey() == null) return false; +#if !TYPE_OBJECT + if(!(entry.getKey() instanceof CLASS_TYPE)) return false; +#endif + RB_TREE_MAP.Entry KEY_VALUE_GENERIC_TYPE subEntry = RB_TREE_MAP.this.findNode((CLASS_TYPE)entry.getKey()); + if(subEntry != null) return Objects.equals(entry.getValue(), VALUE_TO_OBJ(subEntry.value)); + } } return false; } @@ -2620,6 +2686,43 @@ public class RB_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_G } #endif + @Override + public boolean equals(Object obj) { + if(obj instanceof Map.Entry) { + if(obj instanceof MAP.Entry) { + MAP.Entry KEY_VALUE_GENERIC_TYPE entry = (MAP.Entry KEY_VALUE_GENERIC_TYPE)obj; +#if TYPE_OBJECT + if(entry.ENTRY_KEY() == null) return false; +#endif + return KEY_EQUALS(key, entry.ENTRY_KEY()) && VALUE_EQUALS(value, entry.ENTRY_VALUE()); + } + Map.Entry entry = (Map.Entry)obj; + Object otherKey = entry.getKey(); + if(otherKey == null) return false; + Object otherValue = entry.getValue(); +#if TYPE_OBJECT && VALUE_OBJECT + return KEY_EQUALS(key, otherKey) && VALUE_EQUALS(value, otherValue); +#else if TYPE_OBJECT + return otherValue instanceof CLASS_VALUE_TYPE && KEY_EQUALS(key, otherKey) && VALUE_EQUALS(value, CLASS_TO_VALUE(otherValue)); +#else if VALUE_OBJECT + return otherKey instanceof CLASS_TYPE && KEY_EQUALS(key, CLASS_TO_KEY(otherKey)) && VALUE_EQUALS(value, otherValue); +#else + return otherKey instanceof CLASS_TYPE && otherValue instanceof CLASS_VALUE_TYPE && KEY_EQUALS(key, CLASS_TO_KEY(otherKey)) && VALUE_EQUALS(value, CLASS_TO_VALUE(otherValue)); +#endif + } + return false; + } + + @Override + public int hashCode() { + return KEY_TO_HASH(key) ^ VALUE_TO_HASH(value); + } + + @Override + public String toString() { + return KEY_TO_STRING(key) + "=" + VALUE_TO_STRING(value); + } + boolean isBlack() { return (state & BLACK) != 0; } diff --git a/src/test/java/speiger/src/collections/objects/map/MapTest.java b/src/test/java/speiger/src/collections/objects/map/MapTest.java deleted file mode 100644 index 3f1d8127..00000000 --- a/src/test/java/speiger/src/collections/objects/map/MapTest.java +++ /dev/null @@ -1,40 +0,0 @@ -package speiger.src.collections.objects.map; - -import java.util.Map; -import java.util.function.Supplier; - -import com.google.common.collect.testing.MapTestSuiteBuilder; -import com.google.common.collect.testing.TestStringMapGenerator; -import com.google.common.collect.testing.features.CollectionFeature; -import com.google.common.collect.testing.features.CollectionSize; -import com.google.common.collect.testing.features.MapFeature; - -import junit.framework.Test; -import junit.framework.TestCase; -import speiger.src.collections.objects.maps.impl.hash.Object2ObjectOpenHashMap; - -@SuppressWarnings("javadoc") -public final class MapTest extends TestCase -{ - public static Test suite() - { - return suite("Object2ObjectOpenHashMap", Object2ObjectOpenHashMap::new); - } - - public static Test suite(String name, Supplier> factory) - { - return MapTestSuiteBuilder.using(new TestStringMapGenerator() - { - @Override - protected Map create(Map.Entry[] entries) - { - Map map = factory.get(); - for(Map.Entry entry : entries) - { - map.put(entry.getKey(), entry.getValue()); - } - return map; - } - }).named(name).withFeatures(CollectionSize.ANY, MapFeature.GENERAL_PURPOSE, MapFeature.ALLOWS_NULL_KEYS, MapFeature.ALLOWS_NULL_VALUES, MapFeature.ALLOWS_ANY_NULL_QUERIES, CollectionFeature.SUPPORTS_ITERATOR_REMOVE).createTestSuite(); - } -} diff --git a/src/test/java/speiger/src/collections/objects/map/ObjectMapTests.java b/src/test/java/speiger/src/collections/objects/map/ObjectMapTests.java new file mode 100644 index 00000000..62e9c698 --- /dev/null +++ b/src/test/java/speiger/src/collections/objects/map/ObjectMapTests.java @@ -0,0 +1,90 @@ +package speiger.src.collections.objects.map; + +import java.util.Comparator; +import java.util.Map; +import java.util.Objects; +import java.util.function.BiFunction; +import java.util.function.Supplier; + +import com.google.common.collect.testing.MapTestSuiteBuilder; +import com.google.common.collect.testing.TestStringMapGenerator; +import com.google.common.collect.testing.features.CollectionFeature; +import com.google.common.collect.testing.features.CollectionSize; +import com.google.common.collect.testing.features.MapFeature; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; +import speiger.src.collections.objects.maps.impl.customHash.Object2ObjectLinkedOpenCustomHashMap; +import speiger.src.collections.objects.maps.impl.customHash.Object2ObjectOpenCustomHashMap; +import speiger.src.collections.objects.maps.impl.hash.Object2ObjectLinkedOpenHashMap; +import speiger.src.collections.objects.maps.impl.hash.Object2ObjectOpenHashMap; +import speiger.src.collections.objects.maps.impl.immutable.ImmutableObject2ObjectOpenHashMap; +import speiger.src.collections.objects.maps.impl.tree.Object2ObjectAVLTreeMap; +import speiger.src.collections.objects.maps.impl.tree.Object2ObjectRBTreeMap; +import speiger.src.collections.objects.utils.ObjectStrategy; + +@SuppressWarnings("javadoc") +public class ObjectMapTests extends TestCase +{ + public static Test suite() + { + TestSuite suite = new TestSuite("Maps"); + suite.addTest(suite("HashMap", Object2ObjectOpenHashMap::new, true)); + suite.addTest(suite("LinkedHashMap", Object2ObjectLinkedOpenHashMap::new, true)); + suite.addTest(suite("CustomHashMap", () -> new Object2ObjectOpenCustomHashMap<>(Strategy.INSTANCE), true)); + suite.addTest(suite("LinkedCustomHashMap", () -> new Object2ObjectLinkedOpenCustomHashMap<>(Strategy.INSTANCE), true)); + suite.addTest(suite("RBTreeMap", () -> new Object2ObjectRBTreeMap(Comparator.naturalOrder()), false)); + suite.addTest(suite("AVLTreeMap", () -> new Object2ObjectAVLTreeMap(Comparator.naturalOrder()), false)); + suite.addTest(immutableSuit("ImmutableMap", ImmutableObject2ObjectOpenHashMap::new)); + return suite; + } + + public static Test suite(String name, Supplier> factory, boolean allowNull) + { + MapTestSuiteBuilder builder = MapTestSuiteBuilder.using(new TestStringMapGenerator() { + @Override + protected Map create(Map.Entry[] entries) { + Map map = factory.get(); + for(Map.Entry entry : entries) { + map.put(entry.getKey(), entry.getValue()); + } + return map; + } + }).named(name).withFeatures(CollectionSize.ANY, MapFeature.GENERAL_PURPOSE, MapFeature.ALLOWS_NULL_VALUES, CollectionFeature.SUPPORTS_ITERATOR_REMOVE); + if(allowNull) builder.withFeatures(MapFeature.ALLOWS_NULL_KEYS, MapFeature.ALLOWS_ANY_NULL_QUERIES); + return builder.createTestSuite(); + } + + public static Test immutableSuit(String name, BiFunction> factory) { + MapTestSuiteBuilder builder = MapTestSuiteBuilder.using(new TestStringMapGenerator() { + @Override + protected Map create(Map.Entry[] entries) { + String[] keys = new String[entries.length]; + String[] values = new String[entries.length]; + for(int i = 0;i + { + static final Strategy INSTANCE = new Strategy(); + + @Override + public int hashCode(String o) { + return Objects.hashCode(o); + } + + @Override + public boolean equals(String key, String value) { + return Objects.equals(key, value); + } + + } +}