Skip to content

Commit 1d0b95f

Browse files
committed
StringMap: Generalize equals/hashCode across implementations
This change rewrites the `equals` and `hashCode` implementations in `SortedArrayStringMap` and `JdkMapAdapterStringMap` to support value-based equality between the two implementations. Fixes #3669.
1 parent 46ec96e commit 1d0b95f

File tree

4 files changed

+65
-22
lines changed

4 files changed

+65
-22
lines changed

log4j-api/src/main/java/org/apache/logging/log4j/util/SortedArrayStringMap.java

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -408,18 +408,19 @@ public boolean equals(final Object obj) {
408408
if (obj == this) {
409409
return true;
410410
}
411-
if (!(obj instanceof SortedArrayStringMap)) {
411+
if (!(obj instanceof StringMap)) {
412412
return false;
413413
}
414-
final SortedArrayStringMap other = (SortedArrayStringMap) obj;
414+
final StringMap other = (StringMap) obj;
415415
if (this.size() != other.size()) {
416416
return false;
417417
}
418-
for (int i = 0; i < size(); i++) {
419-
if (!Objects.equals(keys[i], other.keys[i])) {
418+
for (int i = 0; i < size; i++) {
419+
String key = keys[i];
420+
if (!other.containsKey(key)) {
420421
return false;
421422
}
422-
if (!Objects.equals(values[i], other.values[i])) {
423+
if (!Objects.equals(getValue(key), other.getValue(key))) {
423424
return false;
424425
}
425426
}
@@ -428,19 +429,13 @@ public boolean equals(final Object obj) {
428429

429430
@Override
430431
public int hashCode() {
431-
int result = 37;
432-
result = HASHVAL * result + size;
433-
result = HASHVAL * result + hashCode(keys, size);
434-
result = HASHVAL * result + hashCode(values, size);
435-
return result;
436-
}
437-
438-
private static int hashCode(final Object[] values, final int length) {
439-
int result = 1;
440-
for (int i = 0; i < length; i++) {
441-
result = HASHVAL * result + (values[i] == null ? 0 : values[i].hashCode());
442-
}
443-
return result;
432+
int[] result = new int[] {HASHVAL};
433+
forEach(
434+
(k, v, r) -> {
435+
r[0] += Objects.hashCode(k) ^ Objects.hashCode(v);
436+
},
437+
result);
438+
return result[0];
444439
}
445440

446441
@Override

log4j-core-test/src/test/java/org/apache/logging/log4j/core/impl/JdkMapAdapterStringMapTest.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import java.util.Map;
3636
import java.util.stream.Stream;
3737
import org.apache.logging.log4j.util.BiConsumer;
38+
import org.apache.logging.log4j.util.SortedArrayStringMap;
3839
import org.apache.logging.log4j.util.TriConsumer;
3940
import org.junit.jupiter.api.Test;
4041
import org.junit.jupiter.params.ParameterizedTest;
@@ -850,6 +851,26 @@ void testForEachTriConsumer() {
850851
assertEquals(state.count, original.size());
851852
}
852853

854+
@Test
855+
void testEqualityWithOtherImplementations() {
856+
final JdkMapAdapterStringMap left = new JdkMapAdapterStringMap();
857+
final SortedArrayStringMap right = new SortedArrayStringMap();
858+
assertEquals(left, right);
859+
assertEquals(left.hashCode(), right.hashCode());
860+
861+
left.putValue("a", "avalue");
862+
left.putValue("B", "Bvalue");
863+
right.putValue("B", "Bvalue");
864+
right.putValue("a", "avalue");
865+
assertEquals(left, right);
866+
assertEquals(left.hashCode(), right.hashCode());
867+
868+
left.remove("a");
869+
right.remove("a");
870+
assertEquals(left, right);
871+
assertEquals(left.hashCode(), right.hashCode());
872+
}
873+
853874
static Stream<Arguments> testImmutability() {
854875
return Stream.of(
855876
Arguments.of(new HashMap<>(), false),

log4j-core/src/main/java/org/apache/logging/log4j/core/impl/JdkMapAdapterStringMap.java

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -219,15 +219,32 @@ public boolean equals(final Object object) {
219219
if (object == this) {
220220
return true;
221221
}
222-
if (!(object instanceof JdkMapAdapterStringMap)) {
222+
if (!(object instanceof StringMap)) {
223223
return false;
224224
}
225-
final JdkMapAdapterStringMap other = (JdkMapAdapterStringMap) object;
226-
return map.equals(other.map) && immutable == other.immutable;
225+
final StringMap other = (StringMap) object;
226+
if (size() != other.size()) {
227+
return false;
228+
}
229+
for (String key : map.keySet()) {
230+
if (!other.containsKey(key)) {
231+
return false;
232+
}
233+
if (!Objects.equals(getValue(key), other.getValue(key))) {
234+
return false;
235+
}
236+
}
237+
return true;
227238
}
228239

229240
@Override
230241
public int hashCode() {
231-
return map.hashCode() + (immutable ? 31 : 0);
242+
int[] result = new int[] {31};
243+
forEach(
244+
(k, v, r) -> {
245+
r[0] += Objects.hashCode(k) ^ Objects.hashCode(v);
246+
},
247+
result);
248+
return result[0];
232249
}
233250
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<entry xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xmlns="https://logging.apache.org/xml/ns"
4+
xsi:schemaLocation="https://logging.apache.org/xml/ns https://logging.apache.org/xml/ns/log4j-changelog-0.xsd"
5+
type="fixed">
6+
<issue id="3669" link="https://github.com/apache/logging-log4j2/issues/3669"/>
7+
<description format="asciidoc">
8+
The two major StringMap implementations, SortedArrayStringMap and JdkMapAdapterStringMap, now support equality comparisons against each other.
9+
</description>
10+
</entry>

0 commit comments

Comments
 (0)