From 85e9a73e85dd5c67541505d42e1a282480aa8b1e Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Mon, 4 May 2020 22:31:18 -0700 Subject: [PATCH] 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 --- .../source/ConfigurationPropertyName.java | 121 ++++++++++-------- .../ConfigurationPropertyNameTests.java | 13 +- 2 files changed, 78 insertions(+), 56 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertyName.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertyName.java index 466f0d0c98..894439c0a3 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertyName.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertyName.java @@ -64,6 +64,8 @@ public final class ConfigurationPropertyName implements Comparable= l2) { - return false; - } - char ch1 = indexed1 ? e1.charAt(i, i1) : Character.toLowerCase(e1.charAt(i, i1)); - char ch2 = indexed2 ? e2.charAt(i, i2) : Character.toLowerCase(e2.charAt(i, i2)); - if (!indexed1 && !ElementsParser.isAlphaNumeric(ch1)) { - i1++; - } - else if (!indexed2 && !ElementsParser.isAlphaNumeric(ch2)) { - i2++; - } - else if (ch1 != ch2) { - return false; - } - else { - i1++; - i2++; - } - } - if (i2 < l2) { - if (indexed2) { - return false; - } - do { - char ch2 = Character.toLowerCase(e2.charAt(i, i2++)); - if (ElementsParser.isAlphaNumeric(ch2)) { + private boolean fastElementEquals(Elements e1, Elements e2, int i) { + int length1 = e1.getLength(i); + int length2 = e2.getLength(i); + if (length1 == length2) { + int i1 = 0; + while (length1-- != 0) { + char ch1 = e1.charAt(i, i1); + char ch2 = e2.charAt(i, i1); + if (ch1 != ch2) { return false; } + i1++; } - while (i2 < l2); + return true; } - return true; + return false; } private boolean dashIgnoringElementEquals(Elements e1, Elements e2, int i) { @@ -404,27 +382,70 @@ public final class ConfigurationPropertyName implements Comparable= l2) { + return false; + } + char ch1 = indexed1 ? e1.charAt(i, i1) : Character.toLowerCase(e1.charAt(i, i1)); + char ch2 = indexed2 ? e2.charAt(i, i2) : Character.toLowerCase(e2.charAt(i, i2)); + if (!indexed1 && !ElementsParser.isAlphaNumeric(ch1)) { i1++; } - return true; + else if (!indexed2 && !ElementsParser.isAlphaNumeric(ch2)) { + i2++; + } + else if (ch1 != ch2) { + return false; + } + else { + i1++; + i2++; + } } - return false; + if (i2 < l2) { + if (indexed2) { + return false; + } + do { + char ch2 = Character.toLowerCase(e2.charAt(i, i2++)); + if (ElementsParser.isAlphaNumeric(ch2)) { + return false; + } + } + while (i2 < l2); + } + return true; } @Override public int hashCode() { - return 0; + int hashCode = this.hashCode; + 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; + } + } + return hashCode; } @Override diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/source/ConfigurationPropertyNameTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/source/ConfigurationPropertyNameTests.java index cbb66a50e6..44aec7c704 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/source/ConfigurationPropertyNameTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/source/ConfigurationPropertyNameTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -545,17 +545,18 @@ class ConfigurationPropertyNameTests { ConfigurationPropertyName n13 = ConfigurationPropertyName.of("f-o-o[b-a-r--]"); ConfigurationPropertyName n14 = ConfigurationPropertyName.of("[1]"); ConfigurationPropertyName n15 = ConfigurationPropertyName.of("[-1]"); - assertThat(n01.hashCode()).isEqualTo(n02.hashCode()); - assertThat(n01.hashCode()).isEqualTo(n02.hashCode()); - assertThat(n01.hashCode()).isEqualTo(n03.hashCode()); - assertThat(n01.hashCode()).isEqualTo(n04.hashCode()); - assertThat(n01.hashCode()).isEqualTo(n11.hashCode()); assertThat((Object) n01).isEqualTo(n01); + assertThat(n01.hashCode()).isEqualTo(n01.hashCode()); assertThat((Object) n01).isEqualTo(n02); + assertThat(n01.hashCode()).isEqualTo(n02.hashCode()); assertThat((Object) n01).isEqualTo(n03); + assertThat(n01.hashCode()).isEqualTo(n03.hashCode()); assertThat((Object) n01).isEqualTo(n04); + assertThat(n01.hashCode()).isEqualTo(n04.hashCode()); assertThat((Object) n11).isEqualTo(n03); + assertThat(n11.hashCode()).isEqualTo(n03.hashCode()); assertThat((Object) n03).isEqualTo(n11); + assertThat(n03.hashCode()).isEqualTo(n11.hashCode()); assertThat((Object) n01).isNotEqualTo(n05); assertThat((Object) n01).isNotEqualTo(n06); assertThat((Object) n07).isNotEqualTo(n08);