Dirty fix of issue with CharSequence equivalence, for correct ChronicleMap.equals() and hashCode() impls

This commit is contained in:
leventov
2015-02-13 13:31:12 +07:00
parent 9580010d61
commit c8f61e3275
4 changed files with 119 additions and 18 deletions
@@ -0,0 +1,51 @@
/*
* Copyright 2014 Higher Frequency Trading http://www.higherfrequencytrading.com
*
* 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 net.openhft.chronicle.hash.impl.util;
import org.jetbrains.annotations.NotNull;
public final class CharSequences {
public static boolean equivalent(@NotNull CharSequence a, @NotNull CharSequence b) {
if (a.equals(b))
return true;
if (a instanceof String)
return ((String) a).contentEquals(b);
if (b instanceof String)
return ((String) b).contentEquals(a);
int len = a.length();
if (len != b.length())
return false;
for (int i = 0; i < len; i++) {
if (a.charAt(i) != b.charAt(i))
return false;
}
return true;
}
public static int hash(@NotNull CharSequence cs) {
if (cs instanceof String)
return cs.hashCode();
int h = 0;
for (int i = 0, len = cs.length(); i < len; i++) {
h = 31 * h + cs.charAt(i);
}
return h;
}
private CharSequences() {}
}
@@ -19,6 +19,9 @@ package net.openhft.chronicle.map;
import net.openhft.chronicle.hash.KeyContext;
import net.openhft.chronicle.hash.function.SerializableFunction;
import net.openhft.chronicle.hash.impl.hashlookup.HashLookupIteration;
import net.openhft.chronicle.hash.impl.util.CharSequences;
import net.openhft.lang.io.Bytes;
import net.openhft.lang.io.MultiStoreBytes;
import org.jetbrains.annotations.NotNull;
import java.io.File;
@@ -33,7 +36,7 @@ import static java.util.Collections.emptyList;
import static net.openhft.chronicle.hash.impl.HashContext.SearchState.DELETED;
import static net.openhft.chronicle.hash.impl.util.Objects.requireNonNull;
public interface AbstractChronicleMap<K, V> extends ChronicleMap<K, V>, Serializable {
interface AbstractChronicleMap<K, V> extends ChronicleMap<K, V>, Serializable {
@Override
VanillaContext<K, ?, ?, V, ?, ?> context(K key);
@@ -370,7 +373,11 @@ public interface AbstractChronicleMap<K, V> extends ChronicleMap<K, V>, Serializ
return false;
try {
return forEachEntryWhile(c -> c.get().equals(m.get(c.key())));
return forEachEntryWhile(c -> {
K k = c.key();
V v = (V) m.get(k instanceof CharSequence ? k.toString() : k);
return v != null && c.valueEqualTo(v);
});
} catch (ClassCastException unused) {
return false;
} catch (NullPointerException unused) {
@@ -380,10 +387,20 @@ public interface AbstractChronicleMap<K, V> extends ChronicleMap<K, V>, Serializ
default int mapHashCode() {
int[] h = new int[1];
forEach((k, v) -> h[0] += k.hashCode() ^ v.hashCode());
forEach((k, v) -> h[0] += hashCode(k) ^ hashCode(v));
return h[0];
}
// TODO quick and dirty. Think about how generic guava/koloboke equivalence interface could be
// used. See also BytesInterop.equivalent() and hash().
public static int hashCode(Object obj) {
if (!(obj instanceof CharSequence)) {
return obj.hashCode();
} else {
return CharSequences.hash((CharSequence) obj);
}
}
default String mapToString() {
if (isEmpty())
return "{}";