Skip to content

Commit 85e9a73

Browse files
committed
Add hashcode support to ConfigurationPropertyName
Provide a hashcode implementation for `ConfigurationPropertyName` so that instances can be stored in Map without them all ending up in the same bucket. See gh-20625
1 parent 5309912 commit 85e9a73

File tree

2 files changed

+64
-42
lines changed

2 files changed

+64
-42
lines changed

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

Lines changed: 57 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ public final class ConfigurationPropertyName implements Comparable<Configuration
6464

6565
private String string;
6666

67+
private int hashCode;
68+
6769
private ConfigurationPropertyName(Elements elements) {
6870
this.elements = elements;
6971
this.uniformElements = new CharSequence[elements.getSize()];
@@ -322,23 +324,39 @@ else if (type1.allowsDashIgnoringEqualityCheck() && type2.allowsDashIgnoringEqua
322324
}
323325
}
324326

325-
private boolean defaultElementEquals(Elements e1, Elements e2, int i) {
327+
private boolean fastElementEquals(Elements e1, Elements e2, int i) {
328+
int length1 = e1.getLength(i);
329+
int length2 = e2.getLength(i);
330+
if (length1 == length2) {
331+
int i1 = 0;
332+
while (length1-- != 0) {
333+
char ch1 = e1.charAt(i, i1);
334+
char ch2 = e2.charAt(i, i1);
335+
if (ch1 != ch2) {
336+
return false;
337+
}
338+
i1++;
339+
}
340+
return true;
341+
}
342+
return false;
343+
}
344+
345+
private boolean dashIgnoringElementEquals(Elements e1, Elements e2, int i) {
326346
int l1 = e1.getLength(i);
327347
int l2 = e2.getLength(i);
328-
boolean indexed1 = e1.getType(i).isIndexed();
329-
boolean indexed2 = e2.getType(i).isIndexed();
330348
int i1 = 0;
331349
int i2 = 0;
332350
while (i1 < l1) {
333351
if (i2 >= l2) {
334352
return false;
335353
}
336-
char ch1 = indexed1 ? e1.charAt(i, i1) : Character.toLowerCase(e1.charAt(i, i1));
337-
char ch2 = indexed2 ? e2.charAt(i, i2) : Character.toLowerCase(e2.charAt(i, i2));
338-
if (!indexed1 && !ElementsParser.isAlphaNumeric(ch1)) {
354+
char ch1 = e1.charAt(i, i1);
355+
char ch2 = e2.charAt(i, i2);
356+
if (ch1 == '-') {
339357
i1++;
340358
}
341-
else if (!indexed2 && !ElementsParser.isAlphaNumeric(ch2)) {
359+
else if (ch2 == '-') {
342360
i2++;
343361
}
344362
else if (ch1 != ch2) {
@@ -350,12 +368,12 @@ else if (ch1 != ch2) {
350368
}
351369
}
352370
if (i2 < l2) {
353-
if (indexed2) {
371+
if (e2.getType(i).isIndexed()) {
354372
return false;
355373
}
356374
do {
357-
char ch2 = Character.toLowerCase(e2.charAt(i, i2++));
358-
if (ElementsParser.isAlphaNumeric(ch2)) {
375+
char ch2 = e2.charAt(i, i2++);
376+
if (ch2 != '-') {
359377
return false;
360378
}
361379
}
@@ -364,21 +382,23 @@ else if (ch1 != ch2) {
364382
return true;
365383
}
366384

367-
private boolean dashIgnoringElementEquals(Elements e1, Elements e2, int i) {
385+
private boolean defaultElementEquals(Elements e1, Elements e2, int i) {
368386
int l1 = e1.getLength(i);
369387
int l2 = e2.getLength(i);
388+
boolean indexed1 = e1.getType(i).isIndexed();
389+
boolean indexed2 = e2.getType(i).isIndexed();
370390
int i1 = 0;
371391
int i2 = 0;
372392
while (i1 < l1) {
373393
if (i2 >= l2) {
374394
return false;
375395
}
376-
char ch1 = e1.charAt(i, i1);
377-
char ch2 = e2.charAt(i, i2);
378-
if (ch1 == '-') {
396+
char ch1 = indexed1 ? e1.charAt(i, i1) : Character.toLowerCase(e1.charAt(i, i1));
397+
char ch2 = indexed2 ? e2.charAt(i, i2) : Character.toLowerCase(e2.charAt(i, i2));
398+
if (!indexed1 && !ElementsParser.isAlphaNumeric(ch1)) {
379399
i1++;
380400
}
381-
else if (ch2 == '-') {
401+
else if (!indexed2 && !ElementsParser.isAlphaNumeric(ch2)) {
382402
i2++;
383403
}
384404
else if (ch1 != ch2) {
@@ -390,12 +410,12 @@ else if (ch1 != ch2) {
390410
}
391411
}
392412
if (i2 < l2) {
393-
if (e2.getType(i).isIndexed()) {
413+
if (indexed2) {
394414
return false;
395415
}
396416
do {
397-
char ch2 = e2.charAt(i, i2++);
398-
if (ch2 != '-') {
417+
char ch2 = Character.toLowerCase(e2.charAt(i, i2++));
418+
if (ElementsParser.isAlphaNumeric(ch2)) {
399419
return false;
400420
}
401421
}
@@ -404,27 +424,28 @@ else if (ch1 != ch2) {
404424
return true;
405425
}
406426

407-
private boolean fastElementEquals(Elements e1, Elements e2, int i) {
408-
int length1 = e1.getLength(i);
409-
int length2 = e2.getLength(i);
410-
if (length1 == length2) {
411-
int i1 = 0;
412-
while (length1-- != 0) {
413-
char ch1 = e1.charAt(i, i1);
414-
char ch2 = e2.charAt(i, i1);
415-
if (ch1 != ch2) {
416-
return false;
427+
@Override
428+
public int hashCode() {
429+
int hashCode = this.hashCode;
430+
Elements elements = this.elements;
431+
if (hashCode == 0 && elements.getSize() != 0) {
432+
for (int elementIndex = 0; elementIndex < elements.getSize(); elementIndex++) {
433+
int elementHashCode = 0;
434+
boolean indexed = elements.getType(elementIndex).isIndexed();
435+
int length = elements.getLength(elementIndex);
436+
for (int i = 0; i < length; i++) {
437+
char ch = elements.charAt(elementIndex, i);
438+
if (!indexed) {
439+
ch = Character.toLowerCase(ch);
440+
}
441+
if (ElementsParser.isAlphaNumeric(ch)) {
442+
elementHashCode = 31 * elementHashCode + ch;
443+
}
417444
}
418-
i1++;
445+
hashCode = 31 * hashCode + elementHashCode;
419446
}
420-
return true;
421447
}
422-
return false;
423-
}
424-
425-
@Override
426-
public int hashCode() {
427-
return 0;
448+
return hashCode;
428449
}
429450

430451
@Override

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/source/ConfigurationPropertyNameTests.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2019 the original author or authors.
2+
* Copyright 2012-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -545,17 +545,18 @@ void equalsAndHashCode() {
545545
ConfigurationPropertyName n13 = ConfigurationPropertyName.of("f-o-o[b-a-r--]");
546546
ConfigurationPropertyName n14 = ConfigurationPropertyName.of("[1]");
547547
ConfigurationPropertyName n15 = ConfigurationPropertyName.of("[-1]");
548-
assertThat(n01.hashCode()).isEqualTo(n02.hashCode());
549-
assertThat(n01.hashCode()).isEqualTo(n02.hashCode());
550-
assertThat(n01.hashCode()).isEqualTo(n03.hashCode());
551-
assertThat(n01.hashCode()).isEqualTo(n04.hashCode());
552-
assertThat(n01.hashCode()).isEqualTo(n11.hashCode());
553548
assertThat((Object) n01).isEqualTo(n01);
549+
assertThat(n01.hashCode()).isEqualTo(n01.hashCode());
554550
assertThat((Object) n01).isEqualTo(n02);
551+
assertThat(n01.hashCode()).isEqualTo(n02.hashCode());
555552
assertThat((Object) n01).isEqualTo(n03);
553+
assertThat(n01.hashCode()).isEqualTo(n03.hashCode());
556554
assertThat((Object) n01).isEqualTo(n04);
555+
assertThat(n01.hashCode()).isEqualTo(n04.hashCode());
557556
assertThat((Object) n11).isEqualTo(n03);
557+
assertThat(n11.hashCode()).isEqualTo(n03.hashCode());
558558
assertThat((Object) n03).isEqualTo(n11);
559+
assertThat(n03.hashCode()).isEqualTo(n11.hashCode());
559560
assertThat((Object) n01).isNotEqualTo(n05);
560561
assertThat((Object) n01).isNotEqualTo(n06);
561562
assertThat((Object) n07).isNotEqualTo(n08);

0 commit comments

Comments
 (0)