4242public final class Permutation
4343 implements Serializable , Iterable <Permutation >, Copyable <Permutation > {
4444
45- private static final long serialVersionUID = 1L ;
45+ private static final long serialVersionUID = 2L ;
4646
4747 /**
4848 * Raw permutation, which should consist of a permutation of the integers in [0,
4949 * permutation.length).
5050 */
5151 private final int [] permutation ;
5252
53+ /*
54+ * Class caches the hashCode the first time hashCode() is called
55+ * to avoid cost of recomputing in applications that rely on HashSets or HashMaps
56+ * of Permutations, etc with heavy use of the hashCode.
57+ */
58+ private transient int hashCode ;
59+
60+ /*
61+ * Flag for validating/invalidating cache of hashCode. All methods
62+ * that change state of Permutation must invalidate the cache.
63+ */
64+ private transient boolean hashCodeIsCached ;
65+
5366 /**
5467 * Initializes a random permutation of n integers. Uses {@link ThreadLocalRandom} as the source of
5568 * efficient random number generation.
@@ -159,6 +172,8 @@ private Permutation(int[] p, boolean validate) {
159172 */
160173 public Permutation (Permutation p ) {
161174 permutation = p .permutation .clone ();
175+ hashCodeIsCached = p .hashCodeIsCached ;
176+ hashCode = p .hashCode ;
162177 }
163178
164179 /**
@@ -197,6 +212,7 @@ public Permutation(Permutation p, int length) {
197212 */
198213 public void apply (PermutationUnaryOperator operator ) {
199214 operator .apply (permutation );
215+ hashCodeIsCached = false ;
200216 }
201217
202218 /**
@@ -206,6 +222,7 @@ public void apply(PermutationUnaryOperator operator) {
206222 */
207223 public void apply (PermutationFullUnaryOperator operator ) {
208224 operator .apply (permutation , this );
225+ hashCodeIsCached = false ;
209226 }
210227
211228 /**
@@ -218,6 +235,8 @@ public void apply(PermutationFullUnaryOperator operator) {
218235 */
219236 public void apply (PermutationBinaryOperator operator , Permutation other ) {
220237 operator .apply (permutation , other .permutation );
238+ hashCodeIsCached = false ;
239+ other .hashCodeIsCached = false ;
221240 }
222241
223242 /**
@@ -230,6 +249,8 @@ public void apply(PermutationBinaryOperator operator, Permutation other) {
230249 */
231250 public void apply (PermutationFullBinaryOperator operator , Permutation other ) {
232251 operator .apply (permutation , other .permutation , this , other );
252+ hashCodeIsCached = false ;
253+ other .hashCodeIsCached = false ;
233254 }
234255
235256 /**
@@ -243,6 +264,7 @@ public void apply(PermutationFullBinaryOperator operator, Permutation other) {
243264 public void applyThenValidate (PermutationUnaryOperator operator ) {
244265 try {
245266 operator .apply (permutation );
267+ hashCodeIsCached = false ;
246268 validate (permutation );
247269 } catch (IllegalArgumentException exception ) {
248270 throw new IllegalPermutationStateException (
@@ -261,6 +283,7 @@ public void applyThenValidate(PermutationUnaryOperator operator) {
261283 public void applyThenValidate (PermutationFullUnaryOperator operator ) {
262284 try {
263285 operator .apply (permutation , this );
286+ hashCodeIsCached = false ;
264287 validate (permutation );
265288 } catch (IllegalArgumentException exception ) {
266289 throw new IllegalPermutationStateException (
@@ -282,6 +305,8 @@ public void applyThenValidate(PermutationFullUnaryOperator operator) {
282305 public void applyThenValidate (PermutationBinaryOperator operator , Permutation other ) {
283306 try {
284307 operator .apply (permutation , other .permutation );
308+ hashCodeIsCached = false ;
309+ other .hashCodeIsCached = false ;
285310 validate (permutation );
286311 validate (other .permutation );
287312 } catch (IllegalArgumentException exception ) {
@@ -305,6 +330,8 @@ public void applyThenValidate(PermutationBinaryOperator operator, Permutation ot
305330 public void applyThenValidate (PermutationFullBinaryOperator operator , Permutation other ) {
306331 try {
307332 operator .apply (permutation , other .permutation , this , other );
333+ hashCodeIsCached = false ;
334+ other .hashCodeIsCached = false ;
308335 validate (permutation );
309336 validate (other .permutation );
310337 } catch (IllegalArgumentException exception ) {
@@ -413,6 +440,7 @@ public Permutation getInversePermutation() {
413440 */
414441 public void invert () {
415442 System .arraycopy (getInverse (), 0 , permutation , 0 , permutation .length );
443+ hashCodeIsCached = false ;
416444 }
417445
418446 /**
@@ -444,6 +472,7 @@ public void scramble(RandomGenerator r) {
444472 permutation [j ] = i ;
445473 }
446474 }
475+ hashCodeIsCached = false ;
447476 }
448477 }
449478
@@ -471,13 +500,14 @@ public void scramble(RandomGenerator r, boolean guaranteeDifferent) {
471500 for (int i = permutation .length - 1 ; i > 1 ; i --) {
472501 int j = RandomIndexer .nextInt (i + 1 , r );
473502 if (i != j ) {
474- swap (i , j );
503+ internalSwap (i , j );
475504 changed = true ;
476505 }
477506 }
478507 if (permutation .length > 1 && (!changed || r .nextBoolean ())) {
479- swap (0 , 1 );
508+ internalSwap (0 , 1 );
480509 }
510+ hashCodeIsCached = false ;
481511 } else {
482512 scramble (r );
483513 }
@@ -509,22 +539,23 @@ public void scramble(int i, int j, RandomGenerator r) {
509539 if (i == j ) {
510540 return ;
511541 }
542+ int k = j ;
512543 if (i > j ) {
513- int temp = i ;
544+ k = i ;
514545 i = j ;
515- j = temp ;
516546 }
517547 boolean changed = false ;
518- for (int k = j ; k > i + 1 ; k --) {
548+ for (; k > i + 1 ; k --) {
519549 int l = i + RandomIndexer .nextInt (k - i + 1 , r );
520550 if (l != k ) {
521- swap (l , k );
551+ internalSwap (l , k );
522552 changed = true ;
523553 }
524554 }
525555 if (!changed || r .nextBoolean ()) {
526- swap (i , i + 1 );
556+ internalSwap (i , i + 1 );
527557 }
558+ hashCodeIsCached = false ;
528559 }
529560
530561 /**
@@ -544,13 +575,14 @@ public void scramble(int[] indexes, RandomGenerator r) {
544575 for (int j = indexes .length - 1 ; j > 1 ; j --) {
545576 int i = RandomIndexer .nextInt (j + 1 , r );
546577 if (i != j ) {
547- swap (indexes [i ], indexes [j ]);
578+ internalSwap (indexes [i ], indexes [j ]);
548579 changed = true ;
549580 }
550581 }
551582 if (!changed || r .nextBoolean ()) {
552- swap (indexes [0 ], indexes [1 ]);
583+ internalSwap (indexes [0 ], indexes [1 ]);
553584 }
585+ hashCodeIsCached = false ;
554586 }
555587 }
556588
@@ -667,6 +699,7 @@ public void swap(int i, int j) {
667699 int temp = permutation [i ];
668700 permutation [i ] = permutation [j ];
669701 permutation [j ] = temp ;
702+ hashCodeIsCached = false ;
670703 }
671704
672705 /**
@@ -688,6 +721,7 @@ public void cycle(int[] indexes) {
688721 permutation [indexes [i - 1 ]] = permutation [indexes [i ]];
689722 }
690723 permutation [indexes [indexes .length - 1 ]] = temp ;
724+ hashCodeIsCached = false ;
691725 }
692726 }
693727
@@ -718,14 +752,16 @@ public void swapBlocks(int a, int b, int i, int j) {
718752 System .arraycopy (permutation , b + 1 , temp , k , m );
719753 System .arraycopy (permutation , a , temp , k + m , b - a + 1 );
720754 System .arraycopy (temp , 0 , permutation , a , temp .length );
755+ hashCodeIsCached = false ;
721756 }
722757 }
723758
724759 /** Reverses the order of the elements in the permutation. */
725760 public void reverse () {
726761 for (int i = 0 , j = permutation .length - 1 ; i < j ; i ++, j --) {
727- swap (i , j );
762+ internalSwap (i , j );
728763 }
764+ hashCodeIsCached = false ;
729765 }
730766
731767 /**
@@ -739,13 +775,14 @@ public void reverse() {
739775 public void reverse (int i , int j ) {
740776 if (i > j ) {
741777 for (; i > j ; i --, j ++) {
742- swap (i , j );
778+ internalSwap (i , j );
743779 }
744780 } else {
745781 for (; i < j ; i ++, j --) {
746- swap (i , j );
782+ internalSwap (i , j );
747783 }
748784 }
785+ hashCodeIsCached = false ;
749786 }
750787
751788 /**
@@ -762,10 +799,12 @@ public void removeAndInsert(int i, int j) {
762799 int n = permutation [i ];
763800 System .arraycopy (permutation , i + 1 , permutation , i , j - i );
764801 permutation [j ] = n ;
802+ hashCodeIsCached = false ;
765803 } else if (i > j ) {
766804 int n = permutation [i ];
767805 System .arraycopy (permutation , j , permutation , j + 1 , i - j );
768806 permutation [j ] = n ;
807+ hashCodeIsCached = false ;
769808 }
770809 }
771810
@@ -784,6 +823,7 @@ public void rotate(int numPositions) {
784823 System .arraycopy (
785824 permutation , numPositions , permutation , 0 , permutation .length - numPositions );
786825 System .arraycopy (temp , 0 , permutation , permutation .length - numPositions , numPositions );
826+ hashCodeIsCached = false ;
787827 }
788828 }
789829
@@ -809,11 +849,13 @@ public void removeAndInsert(int i, int size, int j) {
809849 System .arraycopy (permutation , j , temp , 0 , i - j );
810850 System .arraycopy (permutation , i , permutation , j , size );
811851 System .arraycopy (temp , 0 , permutation , j + size , i - j );
852+ hashCodeIsCached = false ;
812853 } else { // Condition is implied by above: if (i < j)
813854 int [] temp = new int [size ];
814855 System .arraycopy (permutation , i , temp , 0 , size );
815856 System .arraycopy (permutation , i + size , permutation , i , j - i );
816857 System .arraycopy (temp , 0 , permutation , j , size );
858+ hashCodeIsCached = false ;
817859 }
818860 }
819861
@@ -831,6 +873,7 @@ public void set(int[] p) {
831873 }
832874 validate (p );
833875 System .arraycopy (p , 0 , permutation , 0 , p .length );
876+ hashCodeIsCached = false ;
834877 }
835878
836879 /**
@@ -892,7 +935,11 @@ public boolean equals(Object other) {
892935 */
893936 @ Override
894937 public int hashCode () {
895- return Arrays .hashCode (permutation );
938+ if (hashCodeIsCached ) {
939+ return hashCode ;
940+ }
941+ hashCodeIsCached = true ;
942+ return hashCode = Arrays .hashCode (permutation );
896943 }
897944
898945 private boolean validate (int [] p ) {
@@ -909,4 +956,15 @@ private boolean validate(int[] p) {
909956 }
910957 return true ;
911958 }
959+
960+ /*
961+ * Use internally, such as from reverse, etc to avoid
962+ * repeatedly invalidating hashCode cache (as well as from
963+ * the PermutationIterator).
964+ */
965+ final void internalSwap (int i , int j ) {
966+ int temp = permutation [i ];
967+ permutation [i ] = permutation [j ];
968+ permutation [j ] = temp ;
969+ }
912970}
0 commit comments