@@ -114,7 +114,8 @@ public Map<String, Object> toMap(Map<String, Object> props, Set<String> excludeN
114114 props .put (names [i ], values [i ]);
115115 }
116116 } else {
117- P : for (int i = 0 ; i < size ; ++i ) {
117+ P :
118+ for (int i = 0 ; i < size ; ++i ) {
118119 String n = names [i ];
119120 if (!excludes .contains (n )) {
120121 for (String p : excludePrefixes ) {
@@ -313,7 +314,37 @@ public boolean equals(Object o) {
313314 if (size () != hash .size ()) {
314315 return false ;
315316 }
316- return map .equals (hash .map );
317+ // Most Properties comparisons are for cases where they are the same or very similar
318+ // so efficiency is fairly important to loading speed. They are commonly ordered in the
319+ // same fashion so take advantage of that in the comparison
320+ Set <Map .Entry <String , Object >> set1 = map .entrySet ();
321+ Iterator <Map .Entry <String , Object >> iter1 = set1 .iterator ();
322+
323+ Set <Map .Entry <String , Object >> set2 = hash .map .entrySet ();
324+ Iterator <Map .Entry <String , Object >> iter2 = set2 .iterator ();
325+ while (iter1 .hasNext ()) {
326+ var entry = iter1 .next ();
327+ // They are the same length so hasNext must be true
328+ Map .Entry <String , Object > next = iter2 .next ();
329+ if (entry .getKey ().equals (next .getKey ())) {
330+ if (!Objects .deepEquals (entry .getValue (), next .getValue ())) {
331+ return false ;
332+ }
333+ continue ;
334+ }
335+ // Different key encountered so must resort to contains for all following values
336+ if (!Objects .deepEquals (entry .getValue (), hash .map .get (entry .getKey ()))) {
337+ return false ;
338+ }
339+ while (iter1 .hasNext ()) {
340+ entry = iter1 .next ();
341+ if (!Objects .deepEquals (entry .getValue (), hash .map .get (entry .getKey ()))) {
342+ return false ;
343+ }
344+ }
345+ return true ;
346+ }
347+ return true ;
317348 }
318349 return super .equals (o );
319350 }
@@ -347,7 +378,8 @@ public Map<String, Object> toMap(Map<String, Object> props, Set<String> excludeN
347378 if (excludes .isEmpty () && excludePrefixes .length == 0 ) {
348379 props .putAll (map );
349380 } else {
350- P : for (Map .Entry <String , Object > entry : map .entrySet ()) {
381+ P :
382+ for (Map .Entry <String , Object > entry : map .entrySet ()) {
351383 String n = entry .getKey ();
352384 if (!excludes .contains (n )) {
353385 for (String p : excludePrefixes ) {
@@ -484,10 +516,10 @@ protected final int makeHash() {
484516 int hash = 5 ;
485517 for (Property <?> prop : this ) {
486518 hash = hash ^ (Property .makeHash (prop .getName (), prop .getValue ())); // position affected
487- // hash would
488- // violate
489- // equal/hash
490- // contract
519+ // hash would
520+ // violate
521+ // equal/hash
522+ // contract
491523 }
492524 return hash ;
493525 }
@@ -737,10 +769,10 @@ public RegexpPropertyMatcher(String name, String value) {
737769 /**
738770 * Constructs a regular expression based matcher.
739771 *
740- * @param name name of the property to search
741- * @param value pattern
772+ * @param name name of the property to search
773+ * @param value pattern
742774 * @param entireMatch whether the matcher should only accept full matches
743- * @param flags flags to use to compile the pattern defined by {@code value}
775+ * @param flags flags to use to compile the pattern defined by {@code value}
744776 */
745777 public RegexpPropertyMatcher (String name , String value , boolean entireMatch , int flags ) {
746778 if (name == null ) {
@@ -857,7 +889,7 @@ public <T> T get(String key, Class<T> clazz) {
857889 * {@link #get(java.lang.String, java.lang.Class)} will throw an exception/assertion on
858890 * non-String values.
859891 *
860- * @param key property key
892+ * @param key property key
861893 * @param defValue value to be returned if the property is not defined or is {@code null}
862894 * @return String representation
863895 */
0 commit comments