99
1010import java .util .AbstractMap ;
1111import java .util .AbstractSet ;
12- import java .util .ArrayList ;
1312import java .util .Collections ;
1413import java .util .Iterator ;
1514import java .util .List ;
@@ -64,25 +63,21 @@ class SmallSortedMap<K extends Comparable<K>, V> extends AbstractMap<K, V> {
6463 * Creates a new instance for mapping FieldDescriptors to their values. The {@link
6564 * #makeImmutable()} implementation will convert the List values of any repeated fields to
6665 * unmodifiable lists.
67- *
68- * @param arraySize The size of the entry array containing the lexicographically smallest
69- * mappings.
7066 */
71- static <FieldDescriptorType extends FieldSet .FieldDescriptorLite <FieldDescriptorType >>
72- SmallSortedMap <FieldDescriptorType , Object > newFieldMap () {
73- return new SmallSortedMap <FieldDescriptorType , Object >() {
67+ static <FieldDescriptorT extends FieldSet .FieldDescriptorLite <FieldDescriptorT >>
68+ SmallSortedMap <FieldDescriptorT , Object > newFieldMap () {
69+ return new SmallSortedMap <FieldDescriptorT , Object >() {
7470 @ Override
75- @ SuppressWarnings ("unchecked" )
7671 public void makeImmutable () {
7772 if (!isImmutable ()) {
7873 for (int i = 0 ; i < getNumArrayEntries (); i ++) {
79- final Map .Entry <FieldDescriptorType , Object > entry = getArrayEntryAt (i );
74+ final Map .Entry <FieldDescriptorT , Object > entry = getArrayEntryAt (i );
8075 if (entry .getKey ().isRepeated ()) {
8176 final List <?> value = (List ) entry .getValue ();
8277 entry .setValue (Collections .unmodifiableList (value ));
8378 }
8479 }
85- for (Map .Entry <FieldDescriptorType , Object > entry : getOverflowEntries ()) {
80+ for (Map .Entry <FieldDescriptorT , Object > entry : getOverflowEntries ()) {
8681 if (entry .getKey ().isRepeated ()) {
8782 final List <?> value = (List ) entry .getValue ();
8883 entry .setValue (Collections .unmodifiableList (value ));
@@ -99,10 +94,14 @@ static <K extends Comparable<K>, V> SmallSortedMap<K, V> newInstanceForTest() {
9994 return new SmallSortedMap <>();
10095 }
10196
102- // The "entry array" is actually a List because generic arrays are not
103- // allowed. ArrayList also nicely handles the entry shifting on inserts and
104- // removes.
105- private List <Entry > entryList ;
97+ // Only has Entry elements inside.
98+ // Can't declare this as Entry[] because Entry is generic, so you get "generic array creation"
99+ // error. Instead, use an Object[], and cast to Entry on read.
100+ // null Object[] means 'empty'.
101+ private Object [] entries ;
102+ // Number of elements in entries that are valid, like ArrayList.size.
103+ private int entriesSize ;
104+
106105 private Map <K , V > overflowEntries ;
107106 private boolean isImmutable ;
108107 // The EntrySet is a stateless view of the Map. It's initialized the first
@@ -112,16 +111,15 @@ static <K extends Comparable<K>, V> SmallSortedMap<K, V> newInstanceForTest() {
112111 private volatile DescendingEntrySet lazyDescendingEntrySet ;
113112
114113 private SmallSortedMap () {
115- this .entryList = Collections .emptyList ();
116114 this .overflowEntries = Collections .emptyMap ();
117115 this .overflowEntriesDescending = Collections .emptyMap ();
118116 }
119117
120118 /** Make this map immutable from this point forward. */
121119 public void makeImmutable () {
122120 if (!isImmutable ) {
123- // Note: There's no need to wrap the entryList in an unmodifiableList
124- // because none of the list 's accessors are exposed. The iterator() of
121+ // Note: There's no need to wrap the entries in an unmodifiableList
122+ // because none of the array 's accessors are exposed. The iterator() of
125123 // overflowEntries, on the other hand, is exposed so it must be made
126124 // unmodifiable.
127125 overflowEntries =
@@ -143,12 +141,17 @@ public boolean isImmutable() {
143141
144142 /** @return The number of entries in the entry array. */
145143 public int getNumArrayEntries () {
146- return entryList . size () ;
144+ return entriesSize ;
147145 }
148146
149147 /** @return The array entry at the given {@code index}. */
150148 public Map .Entry <K , V > getArrayEntryAt (int index ) {
151- return entryList .get (index );
149+ if (index >= entriesSize ) {
150+ throw new ArrayIndexOutOfBoundsException (index );
151+ }
152+ @ SuppressWarnings ("unchecked" )
153+ Entry e = (Entry ) entries [index ];
154+ return e ;
152155 }
153156
154157 /** @return There number of overflow entries. */
@@ -165,7 +168,7 @@ public Iterable<Map.Entry<K, V>> getOverflowEntries() {
165168
166169 @ Override
167170 public int size () {
168- return entryList . size () + overflowEntries .size ();
171+ return entriesSize + overflowEntries .size ();
169172 }
170173
171174 /**
@@ -191,7 +194,9 @@ public V get(Object o) {
191194 final K key = (K ) o ;
192195 final int index = binarySearchInArray (key );
193196 if (index >= 0 ) {
194- return entryList .get (index ).getValue ();
197+ @ SuppressWarnings ("unchecked" )
198+ Entry e = (Entry ) entries [index ];
199+ return e .getValue ();
195200 }
196201 return overflowEntries .get (key );
197202 }
@@ -202,7 +207,9 @@ public V put(K key, V value) {
202207 final int index = binarySearchInArray (key );
203208 if (index >= 0 ) {
204209 // Replace existing array entry.
205- return entryList .get (index ).setValue (value );
210+ @ SuppressWarnings ("unchecked" )
211+ Entry e = (Entry ) entries [index ];
212+ return e .setValue (value );
206213 }
207214 ensureEntryArrayMutable ();
208215 final int insertionPoint = -(index + 1 );
@@ -211,20 +218,26 @@ public V put(K key, V value) {
211218 return getOverflowEntriesMutable ().put (key , value );
212219 }
213220 // Insert new Entry in array.
214- if (entryList . size () == DEFAULT_FIELD_MAP_ARRAY_SIZE ) {
221+ if (entriesSize == DEFAULT_FIELD_MAP_ARRAY_SIZE ) {
215222 // Shift the last array entry into overflow.
216- final Entry lastEntryInArray = entryList .remove (DEFAULT_FIELD_MAP_ARRAY_SIZE - 1 );
223+ @ SuppressWarnings ("unchecked" )
224+ final Entry lastEntryInArray = (Entry ) entries [DEFAULT_FIELD_MAP_ARRAY_SIZE - 1 ];
225+ entriesSize --;
217226 getOverflowEntriesMutable ().put (lastEntryInArray .getKey (), lastEntryInArray .getValue ());
218227 }
219- entryList .add (insertionPoint , new Entry (key , value ));
228+ System .arraycopy (
229+ entries , insertionPoint , entries , insertionPoint + 1 , entries .length - insertionPoint - 1 );
230+ entries [insertionPoint ] = new Entry (key , value );
231+ entriesSize ++;
220232 return null ;
221233 }
222234
223235 @ Override
224236 public void clear () {
225237 checkMutable ();
226- if (!entryList .isEmpty ()) {
227- entryList .clear ();
238+ if (entriesSize != 0 ) {
239+ entries = null ;
240+ entriesSize = 0 ;
228241 }
229242 if (!overflowEntries .isEmpty ()) {
230243 overflowEntries .clear ();
@@ -256,12 +269,17 @@ public V remove(Object o) {
256269
257270 private V removeArrayEntryAt (int index ) {
258271 checkMutable ();
259- final V removed = entryList .remove (index ).getValue ();
272+ @ SuppressWarnings ("unchecked" )
273+ final V removed = ((Entry ) entries [index ]).getValue ();
274+ // shift items across
275+ System .arraycopy (entries , index + 1 , entries , index , entriesSize - index - 1 );
276+ entriesSize --;
260277 if (!overflowEntries .isEmpty ()) {
261278 // Shift the first entry in the overflow to be the last entry in the
262279 // array.
263280 final Iterator <Map .Entry <K , V >> iterator = getOverflowEntriesMutable ().entrySet ().iterator ();
264- entryList .add (new Entry (iterator .next ()));
281+ entries [entriesSize ] = new Entry (iterator .next ());
282+ entriesSize ++;
265283 iterator .remove ();
266284 }
267285 return removed ;
@@ -274,13 +292,14 @@ private V removeArrayEntryAt(int index) {
274292 */
275293 private int binarySearchInArray (K key ) {
276294 int left = 0 ;
277- int right = entryList . size () - 1 ;
295+ int right = entriesSize - 1 ;
278296
279297 // Optimization: For the common case in which entries are added in
280298 // ascending tag order, check the largest element in the array before
281299 // doing a full binary search.
282300 if (right >= 0 ) {
283- int cmp = key .compareTo (entryList .get (right ).getKey ());
301+ @ SuppressWarnings ("unchecked" )
302+ int cmp = key .compareTo (((Entry ) entries [right ]).getKey ());
284303 if (cmp > 0 ) {
285304 return -(right + 2 ); // Insert point is after "right".
286305 } else if (cmp == 0 ) {
@@ -290,7 +309,8 @@ private int binarySearchInArray(K key) {
290309
291310 while (left <= right ) {
292311 int mid = (left + right ) / 2 ;
293- int cmp = key .compareTo (entryList .get (mid ).getKey ());
312+ @ SuppressWarnings ("unchecked" )
313+ int cmp = key .compareTo (((Entry ) entries [mid ]).getKey ());
294314 if (cmp < 0 ) {
295315 right = mid - 1 ;
296316 } else if (cmp > 0 ) {
@@ -344,11 +364,13 @@ private SortedMap<K, V> getOverflowEntriesMutable() {
344364 return (SortedMap <K , V >) overflowEntries ;
345365 }
346366
347- /** Lazily creates the entry list. Any code that adds to the list must first call this method. */
367+ /**
368+ * Lazily creates the entry array. Any code that adds to the array must first call this method.
369+ */
348370 private void ensureEntryArrayMutable () {
349371 checkMutable ();
350- if (entryList . isEmpty () && !( entryList instanceof ArrayList ) ) {
351- entryList = new ArrayList <>( DEFAULT_FIELD_MAP_ARRAY_SIZE ) ;
372+ if (entries == null ) {
373+ entries = new Object [ DEFAULT_FIELD_MAP_ARRAY_SIZE ] ;
352374 }
353375 }
354376
@@ -498,7 +520,7 @@ private class EntryIterator implements Iterator<Map.Entry<K, V>> {
498520
499521 @ Override
500522 public boolean hasNext () {
501- return (pos + 1 ) < entryList . size ()
523+ return (pos + 1 ) < entriesSize
502524 || (!overflowEntries .isEmpty () && getOverflowIterator ().hasNext ());
503525 }
504526
@@ -507,8 +529,10 @@ public Map.Entry<K, V> next() {
507529 nextCalledBeforeRemove = true ;
508530 // Always increment pos so that we know whether the last returned value
509531 // was from the array or from overflow.
510- if (++pos < entryList .size ()) {
511- return entryList .get (pos );
532+ if (++pos < entriesSize ) {
533+ @ SuppressWarnings ("unchecked" )
534+ Entry e = (Entry ) entries [pos ];
535+ return e ;
512536 }
513537 return getOverflowIterator ().next ();
514538 }
@@ -521,7 +545,7 @@ public void remove() {
521545 nextCalledBeforeRemove = false ;
522546 checkMutable ();
523547
524- if (pos < entryList . size () ) {
548+ if (pos < entriesSize ) {
525549 removeArrayEntryAt (pos --);
526550 } else {
527551 getOverflowIterator ().remove ();
@@ -547,20 +571,22 @@ private Iterator<Map.Entry<K, V>> getOverflowIterator() {
547571 */
548572 private class DescendingEntryIterator implements Iterator <Map .Entry <K , V >> {
549573
550- private int pos = entryList . size () ;
574+ private int pos = entriesSize ;
551575 private Iterator <Map .Entry <K , V >> lazyOverflowIterator ;
552576
553577 @ Override
554578 public boolean hasNext () {
555- return (pos > 0 && pos <= entryList . size () ) || getOverflowIterator ().hasNext ();
579+ return (pos > 0 && pos <= entriesSize ) || getOverflowIterator ().hasNext ();
556580 }
557581
558582 @ Override
559583 public Map .Entry <K , V > next () {
560584 if (getOverflowIterator ().hasNext ()) {
561585 return getOverflowIterator ().next ();
562586 }
563- return entryList .get (--pos );
587+ @ SuppressWarnings ("unchecked" )
588+ Entry e = (Entry ) entries [--pos ];
589+ return e ;
564590 }
565591
566592 @ Override
@@ -621,7 +647,7 @@ public int hashCode() {
621647 int h = 0 ;
622648 final int listSize = getNumArrayEntries ();
623649 for (int i = 0 ; i < listSize ; i ++) {
624- h += entryList . get ( i ) .hashCode ();
650+ h += entries [ i ] .hashCode ();
625651 }
626652 // Avoid the iterator allocation if possible.
627653 if (getNumOverflowEntries () > 0 ) {
0 commit comments