forked from Speiger/Primitive-Collections
Found something in FastUtil i needed to address too. BulkFixes coming.
- Added: Guava TestSuit - Fixed: HashCode and toString method would crash if the Object Key/Value was null - Added: AbstractTypeCollection now delegates the contains check to type-specific Collections if it detects it. - 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.
This commit is contained in:
parent
52caa9cdd2
commit
362838c434
11
Changelog.md
11
Changelog.md
|
@ -12,6 +12,17 @@
|
|||
- Fixed: EnumMaps didn't keep track of their size and now got proper care and implementations as needed. There might be more work required but at least the core functionality is now up to date.
|
||||
- Added: Tests for the new Stream replace functions to ensure no bugs are left.
|
||||
- Fixed: Custom HashSet reduce function with a default value was checking incorrectly for present keys.
|
||||
- Added: Guava TestSuit
|
||||
- Fixed: HashCode and toString method would crash if the Object Key/Value was null
|
||||
- Added: AbstractTypeCollection now delegates the contains check to type-specific Collections if it detects it.
|
||||
- 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.
|
||||
|
||||
### Version 0.4.5
|
||||
- Added: removeAll/retainAll(Collection c, Consumer r) which receives all the elements that got deleted from the collection
|
||||
|
|
|
@ -45,6 +45,8 @@ dependencies {
|
|||
builderCompile 'de.speiger:Simple-Code-Generator:1.0.5'
|
||||
runtimeOnly 'de.speiger:Simple-Code-Generator:1.0.5'
|
||||
testImplementation 'junit:junit:4.12'
|
||||
testImplementation 'com.google.guava:guava-testlib:31.0.1-jre'
|
||||
|
||||
}
|
||||
|
||||
task generateSource(type: JavaExec) {
|
||||
|
|
|
@ -128,8 +128,8 @@ public class GlobalVariables
|
|||
addInjectMapper("OBJ_TO_"+fix, type.isObject() ? "%s" : "%s."+type.getKeyType(value)+"Value()").removeBraces();
|
||||
addInjectMapper("CLASS_TO_"+fix, type.isObject() ? "("+type.getKeyType(value)+")%s" : "(("+type.getClassType(value)+")%s)."+type.getKeyType(value)+"Value()").removeBraces();
|
||||
|
||||
addInjectMapper(fix+"_TO_HASH", type.isObject() ? "%s.hashCode()" : type.getClassType(value)+".hashCode(%s)").removeBraces();
|
||||
addInjectMapper(fix+"_TO_STRING", type.isObject() ? "%s.toString()" : type.getClassType(value)+".toString(%s)").removeBraces();
|
||||
addInjectMapper(fix+"_TO_HASH", type.isObject() ? "Objects.hashCode(%s)" : type.getClassType(value)+".hashCode(%s)").removeBraces();
|
||||
addInjectMapper(fix+"_TO_STRING", type.isObject() ? "Objects.toString(%s)" : type.getClassType(value)+".toString(%s)").removeBraces();
|
||||
|
||||
addSimpleMapper("CAST_"+fix+"_ARRAY ", type.isObject() ? "("+fix+"_TYPE[])" : "");
|
||||
addSimpleMapper("EMPTY_"+fix+"_ARRAY", type.isObject() ? "("+fix+"_TYPE[])ARRAYS.EMPTY_ARRAY" : "ARRAYS.EMPTY_ARRAY");
|
||||
|
|
|
@ -90,6 +90,12 @@ public abstract class ABSTRACT_COLLECTION KEY_GENERIC_TYPE extends AbstractColle
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAll(Collection<?> c) {
|
||||
Objects.requireNonNull(c);
|
||||
return c instanceof COLLECTION ? containsAll((COLLECTION KEY_GENERIC_TYPE)c) : super.containsAll(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation iterates over the elements of the collection and checks if they are stored in this collection
|
||||
* @param c the elements that should be checked for
|
||||
|
|
|
@ -504,7 +504,7 @@ public abstract class ABSTRACT_MAP KEY_VALUE_GENERIC_TYPE extends AbstractMap<CL
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return KEY_TO_STRING(key) + "->" + VALUE_TO_STRING(value);
|
||||
return KEY_TO_STRING(key) + "=" + VALUE_TO_STRING(value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -278,8 +278,7 @@ public class HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_GENE
|
|||
#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(KEY_EQUALS_NOT_NULL(keys[i]) && VALUE_EQUALS(values[i], value)) return true;
|
||||
return false;
|
||||
}
|
||||
|
@ -288,9 +287,12 @@ public class HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_GENE
|
|||
@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 TYPE_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;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -481,7 +483,13 @@ 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 = mappingFunction.GET_VALUE(key);
|
||||
if(VALUE_EQUALS(newValue, getDefaultReturnValue())) return newValue;
|
||||
values[index] = newValue;
|
||||
}
|
||||
return newValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -499,7 +507,7 @@ public class HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_GENE
|
|||
@Override
|
||||
public VALUE_TYPE COMPUTE_IF_PRESENT(KEY_TYPE key, UNARY_OPERATOR KEY_VALUE_GENERIC_TYPE 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);
|
||||
|
@ -511,8 +519,12 @@ public class HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_GENE
|
|||
|
||||
@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);
|
||||
|
@ -528,7 +540,7 @@ public class HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_GENE
|
|||
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);
|
||||
|
@ -744,7 +756,7 @@ public class HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_GENE
|
|||
|
||||
@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]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -928,8 +940,16 @@ public class HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_GENE
|
|||
@Override
|
||||
public boolean contains(Object o) {
|
||||
if(o instanceof Map.Entry) {
|
||||
if(o instanceof MAP.Entry) return HASH_MAP.this.containsKey(((MAP.Entry KEY_VALUE_GENERIC_TYPE)o).ENTRY_KEY());
|
||||
return 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 = HASH_MAP.this.findIndex(entry.ENTRY_KEY());
|
||||
if(index >= 0) return VALUE_EQUALS(entry.ENTRY_VALUE(), HASH_MAP.this.values[index]);
|
||||
}
|
||||
else {
|
||||
Map.Entry<?, ?> entry = (Map.Entry<?, ?>)o;
|
||||
int index = HASH_MAP.this.findIndex(entry.getKey());
|
||||
if(index >= 0) return Objects.equals(entry.getValue(), VALUE_TO_OBJ(HASH_MAP.this.values[index]));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -958,7 +978,7 @@ public class HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_GENE
|
|||
@Override
|
||||
public boolean remove(Object o) {
|
||||
int oldSize = size;
|
||||
remove(o);
|
||||
HASH_MAP.this.remove(o);
|
||||
return size != oldSize;
|
||||
}
|
||||
|
||||
|
@ -971,7 +991,7 @@ public class HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_GENE
|
|||
@Override
|
||||
public boolean remove(KEY_TYPE o) {
|
||||
int oldSize = size;
|
||||
remove(o);
|
||||
HASH_MAP.this.remove(o);
|
||||
return size != oldSize;
|
||||
}
|
||||
|
||||
|
@ -1366,9 +1386,9 @@ public class HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_GENE
|
|||
keys[nullIndex] = EMPTY_KEY_VALUE;
|
||||
values[nullIndex] = EMPTY_VALUE;
|
||||
}
|
||||
else if(pos >= 0) shiftKeys(pos);
|
||||
else if(lastReturned >= 0) shiftKeys(lastReturned);
|
||||
else {
|
||||
HASH_MAP.this.remove(wrapped.GET_KEY(-pos - 1));
|
||||
HASH_MAP.this.remove(wrapped.GET_KEY(-lastReturned - 1));
|
||||
return;
|
||||
}
|
||||
size--;
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
package speiger.src.collections.PACKAGE.maps.interfaces;
|
||||
|
||||
import java.util.Map;
|
||||
#if VALUE_OBJECT && !TYPE_OBJECT
|
||||
import java.util.Objects;
|
||||
#endif
|
||||
#if !TYPE_BOOLEAN
|
||||
import java.util.Collection;
|
||||
import java.util.Arrays;
|
||||
|
@ -414,30 +412,38 @@ public interface MAP KEY_VALUE_GENERIC_TYPE extends Map<CLASS_TYPE, CLASS_VALUE_
|
|||
@Override
|
||||
@Primitive
|
||||
public default void replaceAll(BiFunction<? super CLASS_TYPE, ? super CLASS_VALUE_TYPE, ? extends CLASS_VALUE_TYPE> mappingFunction) {
|
||||
Objects.requireNonNull(mappingFunction);
|
||||
REPLACE_VALUES(mappingFunction instanceof UNARY_OPERATOR ? (UNARY_OPERATOR KEY_VALUE_GENERIC_TYPE)mappingFunction : (K, V) -> OBJ_TO_VALUE(mappingFunction.apply(KEY_TO_OBJ(K), VALUE_TO_OBJ(V))));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Primitive
|
||||
public default CLASS_VALUE_TYPE compute(CLASS_TYPE key, BiFunction<? super CLASS_TYPE, ? super CLASS_VALUE_TYPE, ? extends CLASS_VALUE_TYPE> mappingFunction) {
|
||||
Objects.requireNonNull(mappingFunction);
|
||||
return VALUE_TO_OBJ(COMPUTE(OBJ_TO_KEY(key), mappingFunction instanceof UNARY_OPERATOR ? (UNARY_OPERATOR KEY_VALUE_GENERIC_TYPE)mappingFunction : (K, V) -> OBJ_TO_VALUE(mappingFunction.apply(KEY_TO_OBJ(K), VALUE_TO_OBJ(V)))));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Primitive
|
||||
public default CLASS_VALUE_TYPE computeIfAbsent(CLASS_TYPE key, Function<? super CLASS_TYPE, ? extends CLASS_VALUE_TYPE> mappingFunction) {
|
||||
Objects.requireNonNull(mappingFunction);
|
||||
return VALUE_TO_OBJ(COMPUTE_IF_ABSENT(OBJ_TO_KEY(key), mappingFunction instanceof FUNCTION ? (FUNCTION KEY_VALUE_GENERIC_TYPE)mappingFunction : K -> OBJ_TO_VALUE(mappingFunction.apply(KEY_TO_OBJ(K)))));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Primitive
|
||||
public default CLASS_VALUE_TYPE computeIfPresent(CLASS_TYPE key, BiFunction<? super CLASS_TYPE, ? super CLASS_VALUE_TYPE, ? extends CLASS_VALUE_TYPE> mappingFunction) {
|
||||
Objects.requireNonNull(mappingFunction);
|
||||
return VALUE_TO_OBJ(COMPUTE_IF_PRESENT(OBJ_TO_KEY(key), mappingFunction instanceof UNARY_OPERATOR ? (UNARY_OPERATOR KEY_VALUE_GENERIC_TYPE)mappingFunction : (K, V) -> OBJ_TO_VALUE(mappingFunction.apply(KEY_TO_OBJ(K), VALUE_TO_OBJ(V)))));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Primitive
|
||||
public default CLASS_VALUE_TYPE merge(CLASS_TYPE key, CLASS_VALUE_TYPE value, BiFunction<? super CLASS_VALUE_TYPE, ? super CLASS_VALUE_TYPE, ? extends CLASS_VALUE_TYPE> mappingFunction) {
|
||||
Objects.requireNonNull(mappingFunction);
|
||||
#if VALUE_OBJECT
|
||||
Objects.requireNonNull(value);
|
||||
#endif
|
||||
return VALUE_TO_OBJ(MERGE(OBJ_TO_KEY(key), OBJ_TO_VALUE(value), mappingFunction instanceof VALUE_UNARY_OPERATOR ? (VALUE_UNARY_OPERATOR VALUE_VALUE_GENERIC_TYPE)mappingFunction : (K, V) -> OBJ_TO_VALUE(mappingFunction.apply(VALUE_TO_OBJ(K), VALUE_TO_OBJ(V)))));
|
||||
}
|
||||
|
||||
|
@ -450,6 +456,7 @@ public interface MAP KEY_VALUE_GENERIC_TYPE extends Map<CLASS_TYPE, CLASS_VALUE_
|
|||
@Override
|
||||
@Primitive
|
||||
public default void forEach(BiConsumer<? super CLASS_TYPE, ? super CLASS_VALUE_TYPE> action) {
|
||||
Objects.requireNonNull(action);
|
||||
forEach(action instanceof BI_CONSUMER ? (BI_CONSUMER KEY_VALUE_GENERIC_TYPE)action : (K, V) -> action.accept(KEY_TO_OBJ(K), VALUE_TO_OBJ(V)));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
package speiger.src.collections.PACKAGE.sets;
|
||||
|
||||
import java.util.Iterator;
|
||||
#if TYPE_OBJECT
|
||||
import java.util.Objects;
|
||||
#endif
|
||||
import java.util.Set;
|
||||
|
||||
import speiger.src.collections.PACKAGE.collections.ABSTRACT_COLLECTION;
|
||||
|
@ -20,10 +21,10 @@ public abstract class ABSTRACT_SET KEY_GENERIC_TYPE extends ABSTRACT_COLLECTION
|
|||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hashCode = 1;
|
||||
int hashCode = 0;
|
||||
ITERATOR KEY_GENERIC_TYPE i = iterator();
|
||||
while(i.hasNext())
|
||||
hashCode = 31 * hashCode + KEY_TO_HASH(i.NEXT());
|
||||
hashCode += KEY_TO_HASH(i.NEXT());
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
|
@ -35,24 +36,10 @@ public abstract class ABSTRACT_SET KEY_GENERIC_TYPE extends ABSTRACT_COLLECTION
|
|||
return false;
|
||||
Set<?> l = (Set<?>)o;
|
||||
if(l.size() != size()) return false;
|
||||
#if !TYPE_OBJECT
|
||||
if(l instanceof SET)
|
||||
{
|
||||
ITERATOR e1 = iterator();
|
||||
ITERATOR e2 = ((SET)l).iterator();
|
||||
while (e1.hasNext() && e2.hasNext()) {
|
||||
if(!(KEY_EQUALS(e1.NEXT(), e2.NEXT())))
|
||||
return false;
|
||||
}
|
||||
return !(e1.hasNext() || e2.hasNext());
|
||||
try {
|
||||
return containsAll(l);
|
||||
} catch (ClassCastException | NullPointerException unused) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
Iterator<CLASS_TYPE> e1 = iterator();
|
||||
Iterator<?> e2 = l.iterator();
|
||||
while (e1.hasNext() && e2.hasNext()) {
|
||||
if(!Objects.equals(e1.next(), e2.next()))
|
||||
return false;
|
||||
}
|
||||
return !(e1.hasNext() || e2.hasNext());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
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<Map<String, String>> factory)
|
||||
{
|
||||
return MapTestSuiteBuilder.using(new TestStringMapGenerator()
|
||||
{
|
||||
@Override
|
||||
protected Map<String, String> create(Map.Entry<String, String>[] entries)
|
||||
{
|
||||
Map<String, String> map = factory.get();
|
||||
for(Map.Entry<String, String> 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();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue