Improve ConfigurationPropertyName equals/hashCode performance
Update `ConfigurationPropertyName` to improve performance of the `equals(...)` and `hashCode()` methods by making the following changes: - Move element hashCode logic to Element and cache the results - Exit the equals method early if hashcodes don't match - Exit the equals method early if toString() values match Closes gh-44857
This commit is contained in:
@@ -291,7 +291,7 @@ public final class ConfigurationPropertyName implements Comparable<Configuration
|
||||
if (getNumberOfElements() >= name.getNumberOfElements()) {
|
||||
return false;
|
||||
}
|
||||
return elementsEqual(name);
|
||||
return endsWithElementsEqualTo(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -357,10 +357,20 @@ public final class ConfigurationPropertyName implements Comparable<Configuration
|
||||
&& other.elements.canShortcutWithSource(ElementType.UNIFORM)) {
|
||||
return toString().equals(other.toString());
|
||||
}
|
||||
return elementsEqual(other);
|
||||
if (hashCode() != other.hashCode()) {
|
||||
return false;
|
||||
}
|
||||
if (toStringMatches(toString(), other.toString())) {
|
||||
return true;
|
||||
}
|
||||
return endsWithElementsEqualTo(other);
|
||||
}
|
||||
|
||||
private boolean elementsEqual(ConfigurationPropertyName name) {
|
||||
private boolean toStringMatches(String s1, String s2) {
|
||||
return s1.hashCode() == s2.hashCode() && s1.equals(s2);
|
||||
}
|
||||
|
||||
private boolean endsWithElementsEqualTo(ConfigurationPropertyName name) {
|
||||
for (int i = this.elements.getSize() - 1; i >= 0; i--) {
|
||||
if (elementDiffers(this.elements, name.elements, i)) {
|
||||
return false;
|
||||
@@ -508,19 +518,7 @@ public final class ConfigurationPropertyName implements Comparable<Configuration
|
||||
Elements elements = this.elements;
|
||||
if (hashCode == 0 && elements.getSize() != 0) {
|
||||
for (int elementIndex = 0; elementIndex < elements.getSize(); elementIndex++) {
|
||||
int elementHashCode = 0;
|
||||
boolean indexed = elements.getType(elementIndex).isIndexed();
|
||||
int length = elements.getLength(elementIndex);
|
||||
for (int i = 0; i < length; i++) {
|
||||
char ch = elements.charAt(elementIndex, i);
|
||||
if (!indexed) {
|
||||
ch = Character.toLowerCase(ch);
|
||||
}
|
||||
if (ElementsParser.isAlphaNumeric(ch)) {
|
||||
elementHashCode = 31 * elementHashCode + ch;
|
||||
}
|
||||
}
|
||||
hashCode = 31 * hashCode + elementHashCode;
|
||||
hashCode = 31 * hashCode + elements.hashCode(elementIndex);
|
||||
}
|
||||
this.hashCode = hashCode;
|
||||
}
|
||||
@@ -737,7 +735,7 @@ public final class ConfigurationPropertyName implements Comparable<Configuration
|
||||
|
||||
private static final ElementType[] NO_TYPE = {};
|
||||
|
||||
public static final Elements EMPTY = new Elements("", 0, NO_POSITION, NO_POSITION, NO_TYPE, null);
|
||||
public static final Elements EMPTY = new Elements("", 0, NO_POSITION, NO_POSITION, NO_TYPE, null, null);
|
||||
|
||||
private final CharSequence source;
|
||||
|
||||
@@ -749,6 +747,8 @@ public final class ConfigurationPropertyName implements Comparable<Configuration
|
||||
|
||||
private final ElementType[] type;
|
||||
|
||||
private final int[] hashCode;
|
||||
|
||||
/**
|
||||
* Contains any resolved elements or can be {@code null} if there aren't any.
|
||||
* Resolved elements allow us to modify the element values in some way (or example
|
||||
@@ -759,30 +759,35 @@ public final class ConfigurationPropertyName implements Comparable<Configuration
|
||||
*/
|
||||
private final CharSequence[] resolved;
|
||||
|
||||
Elements(CharSequence source, int size, int[] start, int[] end, ElementType[] type, CharSequence[] resolved) {
|
||||
Elements(CharSequence source, int size, int[] start, int[] end, ElementType[] type, int[] hashCode,
|
||||
CharSequence[] resolved) {
|
||||
this.source = source;
|
||||
this.size = size;
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
this.type = type;
|
||||
this.hashCode = (hashCode != null) ? hashCode : new int[size];
|
||||
this.resolved = resolved;
|
||||
}
|
||||
|
||||
Elements append(Elements additional) {
|
||||
int size = this.size + additional.size;
|
||||
ElementType[] type = new ElementType[size];
|
||||
int[] hashCode = new int[size];
|
||||
System.arraycopy(this.type, 0, type, 0, this.size);
|
||||
System.arraycopy(additional.type, 0, type, this.size, additional.size);
|
||||
System.arraycopy(this.hashCode, 0, hashCode, 0, this.size);
|
||||
System.arraycopy(additional.hashCode, 0, hashCode, this.size, additional.size);
|
||||
CharSequence[] resolved = newResolved(0, size);
|
||||
for (int i = 0; i < additional.size; i++) {
|
||||
resolved[this.size + i] = additional.get(i);
|
||||
}
|
||||
return new Elements(this.source, size, this.start, this.end, type, resolved);
|
||||
return new Elements(this.source, size, this.start, this.end, type, hashCode, resolved);
|
||||
}
|
||||
|
||||
Elements chop(int size) {
|
||||
CharSequence[] resolved = newResolved(0, size);
|
||||
return new Elements(this.source, size, this.start, this.end, this.type, resolved);
|
||||
return new Elements(this.source, size, this.start, this.end, this.type, this.hashCode, resolved);
|
||||
}
|
||||
|
||||
Elements subElements(int offset) {
|
||||
@@ -794,7 +799,9 @@ public final class ConfigurationPropertyName implements Comparable<Configuration
|
||||
System.arraycopy(this.end, offset, end, 0, size);
|
||||
ElementType[] type = new ElementType[size];
|
||||
System.arraycopy(this.type, offset, type, 0, size);
|
||||
return new Elements(this.source, size, start, end, type, resolved);
|
||||
int[] hashCode = new int[size];
|
||||
System.arraycopy(this.hashCode, offset, hashCode, 0, size);
|
||||
return new Elements(this.source, size, start, end, type, hashCode, resolved);
|
||||
}
|
||||
|
||||
private CharSequence[] newResolved(int offset, int size) {
|
||||
@@ -839,6 +846,25 @@ public final class ConfigurationPropertyName implements Comparable<Configuration
|
||||
return this.type[index];
|
||||
}
|
||||
|
||||
int hashCode(int index) {
|
||||
int hashCode = this.hashCode[index];
|
||||
if (hashCode == 0) {
|
||||
boolean indexed = getType(index).isIndexed();
|
||||
int length = getLength(index);
|
||||
for (int i = 0; i < length; i++) {
|
||||
char ch = charAt(index, i);
|
||||
if (!indexed) {
|
||||
ch = Character.toLowerCase(ch);
|
||||
}
|
||||
if (ElementsParser.isAlphaNumeric(ch)) {
|
||||
hashCode = 31 * hashCode + ch;
|
||||
}
|
||||
}
|
||||
this.hashCode[index] = hashCode;
|
||||
}
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
CharSequence getSource() {
|
||||
return this.source;
|
||||
}
|
||||
@@ -951,7 +977,7 @@ public final class ConfigurationPropertyName implements Comparable<Configuration
|
||||
type = ElementType.NON_UNIFORM;
|
||||
}
|
||||
add(start, length, type, valueProcessor);
|
||||
return new Elements(this.source, this.size, this.start, this.end, this.type, this.resolved);
|
||||
return new Elements(this.source, this.size, this.start, this.end, this.type, null, this.resolved);
|
||||
}
|
||||
|
||||
private ElementType updateType(ElementType existingType, char ch, int index) {
|
||||
|
||||
Reference in New Issue
Block a user