Skip to content

Commit 81dee54

Browse files
committed
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
1 parent 6431833 commit 81dee54

File tree

1 file changed

+48
-22
lines changed

1 file changed

+48
-22
lines changed

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertyName.java

+48-22
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ public boolean isAncestorOf(ConfigurationPropertyName name) {
291291
if (getNumberOfElements() >= name.getNumberOfElements()) {
292292
return false;
293293
}
294-
return elementsEqual(name);
294+
return endsWithElementsEqualTo(name);
295295
}
296296

297297
@Override
@@ -357,10 +357,20 @@ public boolean equals(Object obj) {
357357
&& other.elements.canShortcutWithSource(ElementType.UNIFORM)) {
358358
return toString().equals(other.toString());
359359
}
360-
return elementsEqual(other);
360+
if (hashCode() != other.hashCode()) {
361+
return false;
362+
}
363+
if (toStringMatches(toString(), other.toString())) {
364+
return true;
365+
}
366+
return endsWithElementsEqualTo(other);
367+
}
368+
369+
private boolean toStringMatches(String s1, String s2) {
370+
return s1.hashCode() == s2.hashCode() && s1.equals(s2);
361371
}
362372

363-
private boolean elementsEqual(ConfigurationPropertyName name) {
373+
private boolean endsWithElementsEqualTo(ConfigurationPropertyName name) {
364374
for (int i = this.elements.getSize() - 1; i >= 0; i--) {
365375
if (elementDiffers(this.elements, name.elements, i)) {
366376
return false;
@@ -508,19 +518,7 @@ public int hashCode() {
508518
Elements elements = this.elements;
509519
if (hashCode == 0 && elements.getSize() != 0) {
510520
for (int elementIndex = 0; elementIndex < elements.getSize(); elementIndex++) {
511-
int elementHashCode = 0;
512-
boolean indexed = elements.getType(elementIndex).isIndexed();
513-
int length = elements.getLength(elementIndex);
514-
for (int i = 0; i < length; i++) {
515-
char ch = elements.charAt(elementIndex, i);
516-
if (!indexed) {
517-
ch = Character.toLowerCase(ch);
518-
}
519-
if (ElementsParser.isAlphaNumeric(ch)) {
520-
elementHashCode = 31 * elementHashCode + ch;
521-
}
522-
}
523-
hashCode = 31 * hashCode + elementHashCode;
521+
hashCode = 31 * hashCode + elements.hashCode(elementIndex);
524522
}
525523
this.hashCode = hashCode;
526524
}
@@ -737,7 +735,7 @@ private static class Elements {
737735

738736
private static final ElementType[] NO_TYPE = {};
739737

740-
public static final Elements EMPTY = new Elements("", 0, NO_POSITION, NO_POSITION, NO_TYPE, null);
738+
public static final Elements EMPTY = new Elements("", 0, NO_POSITION, NO_POSITION, NO_TYPE, null, null);
741739

742740
private final CharSequence source;
743741

@@ -749,6 +747,8 @@ private static class Elements {
749747

750748
private final ElementType[] type;
751749

750+
private final int[] hashCode;
751+
752752
/**
753753
* Contains any resolved elements or can be {@code null} if there aren't any.
754754
* Resolved elements allow us to modify the element values in some way (or example
@@ -759,30 +759,35 @@ private static class Elements {
759759
*/
760760
private final CharSequence[] resolved;
761761

762-
Elements(CharSequence source, int size, int[] start, int[] end, ElementType[] type, CharSequence[] resolved) {
762+
Elements(CharSequence source, int size, int[] start, int[] end, ElementType[] type, int[] hashCode,
763+
CharSequence[] resolved) {
763764
this.source = source;
764765
this.size = size;
765766
this.start = start;
766767
this.end = end;
767768
this.type = type;
769+
this.hashCode = (hashCode != null) ? hashCode : new int[size];
768770
this.resolved = resolved;
769771
}
770772

771773
Elements append(Elements additional) {
772774
int size = this.size + additional.size;
773775
ElementType[] type = new ElementType[size];
776+
int[] hashCode = new int[size];
774777
System.arraycopy(this.type, 0, type, 0, this.size);
775778
System.arraycopy(additional.type, 0, type, this.size, additional.size);
779+
System.arraycopy(this.hashCode, 0, hashCode, 0, this.size);
780+
System.arraycopy(additional.hashCode, 0, hashCode, this.size, additional.size);
776781
CharSequence[] resolved = newResolved(0, size);
777782
for (int i = 0; i < additional.size; i++) {
778783
resolved[this.size + i] = additional.get(i);
779784
}
780-
return new Elements(this.source, size, this.start, this.end, type, resolved);
785+
return new Elements(this.source, size, this.start, this.end, type, hashCode, resolved);
781786
}
782787

783788
Elements chop(int size) {
784789
CharSequence[] resolved = newResolved(0, size);
785-
return new Elements(this.source, size, this.start, this.end, this.type, resolved);
790+
return new Elements(this.source, size, this.start, this.end, this.type, this.hashCode, resolved);
786791
}
787792

788793
Elements subElements(int offset) {
@@ -794,7 +799,9 @@ Elements subElements(int offset) {
794799
System.arraycopy(this.end, offset, end, 0, size);
795800
ElementType[] type = new ElementType[size];
796801
System.arraycopy(this.type, offset, type, 0, size);
797-
return new Elements(this.source, size, start, end, type, resolved);
802+
int[] hashCode = new int[size];
803+
System.arraycopy(this.hashCode, offset, hashCode, 0, size);
804+
return new Elements(this.source, size, start, end, type, hashCode, resolved);
798805
}
799806

800807
private CharSequence[] newResolved(int offset, int size) {
@@ -839,6 +846,25 @@ ElementType getType(int index) {
839846
return this.type[index];
840847
}
841848

849+
int hashCode(int index) {
850+
int hashCode = this.hashCode[index];
851+
if (hashCode == 0) {
852+
boolean indexed = getType(index).isIndexed();
853+
int length = getLength(index);
854+
for (int i = 0; i < length; i++) {
855+
char ch = charAt(index, i);
856+
if (!indexed) {
857+
ch = Character.toLowerCase(ch);
858+
}
859+
if (ElementsParser.isAlphaNumeric(ch)) {
860+
hashCode = 31 * hashCode + ch;
861+
}
862+
}
863+
this.hashCode[index] = hashCode;
864+
}
865+
return hashCode;
866+
}
867+
842868
CharSequence getSource() {
843869
return this.source;
844870
}
@@ -951,7 +977,7 @@ else if (!type.isIndexed() && ch == this.separator) {
951977
type = ElementType.NON_UNIFORM;
952978
}
953979
add(start, length, type, valueProcessor);
954-
return new Elements(this.source, this.size, this.start, this.end, this.type, this.resolved);
980+
return new Elements(this.source, this.size, this.start, this.end, this.type, null, this.resolved);
955981
}
956982

957983
private ElementType updateType(ElementType existingType, char ch, int index) {

0 commit comments

Comments
 (0)