3333import  java .io .Serializable ;
3434import  java .util .AbstractMap ;
3535import  java .util .Arrays ;
36+ import  java .util .BitSet ;
3637import  java .util .Collection ;
3738import  java .util .Collections ;
3839import  java .util .Comparator ;
40+ import  java .util .HashSet ;
3941import  java .util .Iterator ;
4042import  java .util .Map ;
4143import  java .util .Map .Entry ;
44+ import  java .util .Set ;
4245import  java .util .SortedMap ;
4346import  javax .annotation .CheckForNull ;
4447import  org .checkerframework .checker .nullness .qual .Nullable ;
@@ -337,7 +340,7 @@ public static <K, V> Builder<K, V> builderWithExpectedSize(int expectedSize) {
337340  }
338341
339342  static  void  checkNoConflict (
340-       boolean  safe , String  conflictDescription , Entry <?, ?>  entry1 , Entry <?, ?>  entry2 ) {
343+       boolean  safe , String  conflictDescription , Object   entry1 , Object  entry2 ) {
341344    if  (!safe ) {
342345      throw  conflictException (conflictDescription , entry1 , entry2 );
343346    }
@@ -384,6 +387,11 @@ public static class Builder<K, V> {
384387    @ Nullable  Object [] alternatingKeysAndValues ;
385388    int  size ;
386389    boolean  entriesUsed ;
390+     /** 
391+      * If non-null, a duplicate key we found in a previous buildKeepingLast() or buildOrThrow() 
392+      * call. A later buildOrThrow() can simply report this duplicate immediately. 
393+      */ 
394+     @ Nullable  DuplicateKey  duplicateKey ;
387395
388396    /** 
389397     * Creates a new builder. The returned builder is equivalent to the builder generated by {@link 
@@ -498,10 +506,46 @@ Builder<K, V> combine(Builder<K, V> other) {
498506      return  this ;
499507    }
500508
501-     /* 
502-      * TODO(kevinb): Should build() and the ImmutableBiMap & ImmutableSortedMap 
503-      * versions throw an IllegalStateException instead? 
504-      */ 
509+     private  ImmutableMap <K , V > build (boolean  throwIfDuplicateKeys ) {
510+       if  (throwIfDuplicateKeys  && duplicateKey  != null ) {
511+         throw  duplicateKey .exception ();
512+       }
513+       /* 
514+        * If entries is full, then this implementation may end up using the entries array 
515+        * directly and writing over the entry objects with non-terminal entries, but this is 
516+        * safe; if this Builder is used further, it will grow the entries array (so it can't 
517+        * affect the original array), and future build() calls will always copy any entry 
518+        * objects that cannot be safely reused. 
519+        */ 
520+       // localAlternatingKeysAndValues is an alias for the alternatingKeysAndValues field, except if 
521+       // we end up removing duplicates in a copy of the array. 
522+       @ Nullable  Object [] localAlternatingKeysAndValues ;
523+       int  localSize  = size ;
524+       if  (valueComparator  == null ) {
525+         localAlternatingKeysAndValues  = alternatingKeysAndValues ;
526+       } else  {
527+         if  (entriesUsed ) {
528+           alternatingKeysAndValues  = Arrays .copyOf (alternatingKeysAndValues , 2  * size );
529+         }
530+         localAlternatingKeysAndValues  = alternatingKeysAndValues ;
531+         if  (!throwIfDuplicateKeys ) {
532+           // We want to retain only the last-put value for any given key, before sorting. 
533+           // This could be improved, but orderEntriesByValue is rather rarely used anyway. 
534+           localAlternatingKeysAndValues  = lastEntryForEachKey (localAlternatingKeysAndValues , size );
535+           if  (localAlternatingKeysAndValues .length  < alternatingKeysAndValues .length ) {
536+             localSize  = localAlternatingKeysAndValues .length  >>> 1 ;
537+           }
538+         }
539+         sortEntries (localAlternatingKeysAndValues , localSize , valueComparator );
540+       }
541+       entriesUsed  = true ;
542+       ImmutableMap <K , V > map  =
543+           RegularImmutableMap .create (localSize , localAlternatingKeysAndValues , this );
544+       if  (throwIfDuplicateKeys  && duplicateKey  != null ) {
545+         throw  duplicateKey .exception ();
546+       }
547+       return  map ;
548+     }
505549
506550    /** 
507551     * Returns a newly-created immutable map. The iteration order of the returned map is the order 
@@ -527,40 +571,84 @@ public ImmutableMap<K, V> build() {
527571     * @throws IllegalArgumentException if duplicate keys were added 
528572     * @since 31.0 
529573     */ 
530-     @ SuppressWarnings ("unchecked" )
531574    public  ImmutableMap <K , V > buildOrThrow () {
532-       /* 
533-        * If entries is full, then this implementation may end up using the entries array 
534-        * directly and writing over the entry objects with non-terminal entries, but this is 
535-        * safe; if this Builder is used further, it will grow the entries array (so it can't 
536-        * affect the original array), and future build() calls will always copy any entry 
537-        * objects that cannot be safely reused. 
538-        */ 
539-       sortEntries ();
540-       entriesUsed  = true ;
541-       return  RegularImmutableMap .create (size , alternatingKeysAndValues );
575+       return  build (true );
542576    }
543577
544-     void  sortEntries () {
545-       if  (valueComparator  != null ) {
546-         if  (entriesUsed ) {
547-           alternatingKeysAndValues  = Arrays .copyOf (alternatingKeysAndValues , 2  * size );
548-         }
549-         Entry <K , V >[] entries  = new  Entry [size ];
550-         for  (int  i  = 0 ; i  < size ; i ++) {
551-           // requireNonNull is safe because the first `2*size` elements have been filled in. 
552-           entries [i ] =
553-               new  AbstractMap .SimpleImmutableEntry <K , V >(
554-                   (K ) requireNonNull (alternatingKeysAndValues [2  * i ]),
555-                   (V ) requireNonNull (alternatingKeysAndValues [2  * i  + 1 ]));
578+     /** 
579+      * Returns a newly-created immutable map, using the last value for any key that was added more 
580+      * than once. The iteration order of the returned map is the order in which entries were 
581+      * inserted into the builder, unless {@link #orderEntriesByValue} was called, in which case 
582+      * entries are sorted by value. If a key was added more than once, it appears in iteration order 
583+      * based on the first time it was added, again unless {@link #orderEntriesByValue} was called. 
584+      * 
585+      * @since NEXT 
586+      */ 
587+     public  ImmutableMap <K , V > buildKeepingLast () {
588+       return  build (false );
589+     }
590+ 
591+     static  <V > void  sortEntries (
592+         @ Nullable  Object [] alternatingKeysAndValues , int  size , Comparator <V > valueComparator ) {
593+       @ SuppressWarnings ({"rawtypes" , "unchecked" })
594+       Entry <Object , V >[] entries  = new  Entry [size ];
595+       for  (int  i  = 0 ; i  < size ; i ++) {
596+         // requireNonNull is safe because the first `2*size` elements have been filled in. 
597+         Object  key  = requireNonNull (alternatingKeysAndValues [2  * i ]);
598+         @ SuppressWarnings ("unchecked" )
599+         V  value  = (V ) requireNonNull (alternatingKeysAndValues [2  * i  + 1 ]);
600+         entries [i ] = new  AbstractMap .SimpleImmutableEntry <Object , V >(key , value );
601+       }
602+       Arrays .sort (
603+           entries , 0 , size , Ordering .from (valueComparator ).onResultOf (Maps .<V >valueFunction ()));
604+       for  (int  i  = 0 ; i  < size ; i ++) {
605+         alternatingKeysAndValues [2  * i ] = entries [i ].getKey ();
606+         alternatingKeysAndValues [2  * i  + 1 ] = entries [i ].getValue ();
607+       }
608+     }
609+ 
610+     private  @ Nullable  Object [] lastEntryForEachKey (
611+         @ Nullable  Object [] localAlternatingKeysAndValues , int  size ) {
612+       Set <Object > seenKeys  = new  HashSet <>();
613+       BitSet  dups  = new  BitSet (); // slots that are overridden by a later duplicate key 
614+       for  (int  i  = size  - 1 ; i  >= 0 ; i --) {
615+         Object  key  = requireNonNull (localAlternatingKeysAndValues [2  * i ]);
616+         if  (!seenKeys .add (key )) {
617+           dups .set (i );
556618        }
557-         Arrays .sort (
558-             entries , 0 , size , Ordering .from (valueComparator ).onResultOf (Maps .<V >valueFunction ()));
559-         for  (int  i  = 0 ; i  < size ; i ++) {
560-           alternatingKeysAndValues [2  * i ] = entries [i ].getKey ();
561-           alternatingKeysAndValues [2  * i  + 1 ] = entries [i ].getValue ();
619+       }
620+       if  (dups .isEmpty ()) {
621+         return  localAlternatingKeysAndValues ;
622+       }
623+       Object [] newAlternatingKeysAndValues  = new  Object [(size  - dups .cardinality ()) * 2 ];
624+       for  (int  inI  = 0 , outI  = 0 ; inI  < size  * 2 ; ) {
625+         if  (dups .get (inI  >>> 1 )) {
626+           inI  += 2 ;
627+         } else  {
628+           newAlternatingKeysAndValues [outI ++] =
629+               requireNonNull (localAlternatingKeysAndValues [inI ++]);
630+           newAlternatingKeysAndValues [outI ++] =
631+               requireNonNull (localAlternatingKeysAndValues [inI ++]);
562632        }
563633      }
634+       return  newAlternatingKeysAndValues ;
635+     }
636+ 
637+     static  final  class  DuplicateKey  {
638+       private  final  Object  key ;
639+       private  final  Object  value1 ;
640+       private  final  Object  value2 ;
641+ 
642+       DuplicateKey (Object  key , Object  value1 , Object  value2 ) {
643+         this .key  = key ;
644+         this .value1  = value1 ;
645+         this .value2  = value2 ;
646+       }
647+ 
648+       IllegalArgumentException  exception () {
649+         return  new  IllegalArgumentException (
650+             "Multiple entries with same key: "  + key  + "="  + value1  + " and "  + key  + "="  + value2 );
651+       }
564652    }
565653  }
566654
0 commit comments