Home | History | Annotate | Download | only in util
      1 /*
      2  * Copyright (C) 2007 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.dexgen.util;
     18 
     19 import java.util.Arrays;
     20 
     21 /**
     22  * Simple list of {@code int}s.
     23  */
     24 public final class IntList extends MutabilityControl {
     25     /** {@code non-null;} immutable, no-element instance */
     26     public static final IntList EMPTY = new IntList(0);
     27 
     28     /** {@code non-null;} array of elements */
     29     private int[] values;
     30 
     31     /** {@code >= 0;} current size of the list */
     32     private int size;
     33 
     34     /** whether the values are currently sorted */
     35     private boolean sorted;
     36 
     37     static {
     38         EMPTY.setImmutable();
     39     }
     40 
     41     /**
     42      * Constructs a new immutable instance with the given element.
     43      *
     44      * @param value the sole value in the list
     45      */
     46     public static IntList makeImmutable(int value) {
     47         IntList result = new IntList(1);
     48 
     49         result.add(value);
     50         result.setImmutable();
     51 
     52         return result;
     53     }
     54 
     55     /**
     56      * Constructs a new immutable instance with the given elements.
     57      *
     58      * @param value0 the first value in the list
     59      * @param value1 the second value in the list
     60      */
     61     public static IntList makeImmutable(int value0, int value1) {
     62         IntList result = new IntList(2);
     63 
     64         result.add(value0);
     65         result.add(value1);
     66         result.setImmutable();
     67 
     68         return result;
     69     }
     70 
     71     /**
     72      * Constructs an empty instance with a default initial capacity.
     73      */
     74     public IntList() {
     75         this(4);
     76     }
     77 
     78     /**
     79      * Constructs an empty instance.
     80      *
     81      * @param initialCapacity {@code >= 0;} initial capacity of the list
     82      */
     83     public IntList(int initialCapacity) {
     84         super(true);
     85 
     86         try {
     87             values = new int[initialCapacity];
     88         } catch (NegativeArraySizeException ex) {
     89             // Translate the exception.
     90             throw new IllegalArgumentException("size < 0");
     91         }
     92 
     93         size = 0;
     94         sorted = true;
     95     }
     96 
     97     /** {@inheritDoc} */
     98     @Override
     99     public int hashCode() {
    100         int result = 0;
    101 
    102         for (int i = 0; i < size; i++) {
    103             result = (result * 31) + values[i];
    104         }
    105 
    106         return result;
    107     }
    108 
    109     /** {@inheritDoc} */
    110     @Override
    111     public boolean equals(Object other) {
    112         if (other == this) {
    113             return true;
    114         }
    115 
    116         if (! (other instanceof IntList)) {
    117             return false;
    118         }
    119 
    120         IntList otherList = (IntList) other;
    121 
    122         if (sorted != otherList.sorted) {
    123             return false;
    124         }
    125 
    126         if (size != otherList.size) {
    127             return false;
    128         }
    129 
    130         for (int i = 0; i < size; i++) {
    131             if (values[i] != otherList.values[i]) {
    132                 return false;
    133             }
    134         }
    135 
    136         return true;
    137     }
    138 
    139     /** {@inheritDoc} */
    140     @Override
    141     public String toString() {
    142         StringBuffer sb = new StringBuffer(size * 5 + 10);
    143 
    144         sb.append('{');
    145 
    146         for (int i = 0; i < size; i++) {
    147             if (i != 0) {
    148                 sb.append(", ");
    149             }
    150             sb.append(values[i]);
    151         }
    152 
    153         sb.append('}');
    154 
    155         return sb.toString();
    156     }
    157 
    158     /**
    159      * Gets the number of elements in this list.
    160      */
    161     public int size() {
    162         return size;
    163     }
    164 
    165     /**
    166      * Gets the indicated value.
    167      *
    168      * @param n {@code >= 0, < size();} which element
    169      * @return the indicated element's value
    170      */
    171     public int get(int n) {
    172         if (n >= size) {
    173             throw new IndexOutOfBoundsException("n >= size()");
    174         }
    175 
    176         try {
    177             return values[n];
    178         } catch (ArrayIndexOutOfBoundsException ex) {
    179             // Translate exception.
    180             throw new IndexOutOfBoundsException("n < 0");
    181         }
    182     }
    183 
    184     /**
    185      * Sets the value at the given index.
    186      *
    187      * @param n {@code >= 0, < size();} which element
    188      * @param value value to store
    189      */
    190     public void set(int n, int value) {
    191         throwIfImmutable();
    192 
    193         if (n >= size) {
    194             throw new IndexOutOfBoundsException("n >= size()");
    195         }
    196 
    197         try {
    198             values[n] = value;
    199             sorted = false;
    200         } catch (ArrayIndexOutOfBoundsException ex) {
    201             // Translate the exception.
    202             if (n < 0) {
    203                 throw new IllegalArgumentException("n < 0");
    204             }
    205         }
    206     }
    207 
    208     /**
    209      * Adds an element to the end of the list. This will increase the
    210      * list's capacity if necessary.
    211      *
    212      * @param value the value to add
    213      */
    214     public void add(int value) {
    215         throwIfImmutable();
    216 
    217         growIfNeeded();
    218 
    219         values[size++] = value;
    220 
    221         if (sorted && (size > 1)) {
    222             sorted = (value >= values[size - 2]);
    223         }
    224     }
    225 
    226     /**
    227      * Inserts element into specified index, moving elements at and above
    228      * that index up one. May not be used to insert at an index beyond the
    229      * current size (that is, insertion as a last element is legal but
    230      * no further).
    231      *
    232      * @param n {@code >= 0, <=size();} index of where to insert
    233      * @param value value to insert
    234      */
    235     public void insert(int n, int value) {
    236         if (n > size) {
    237             throw new IndexOutOfBoundsException("n > size()");
    238         }
    239 
    240         growIfNeeded();
    241 
    242         System.arraycopy (values, n, values, n+1, size - n);
    243         values[n] = value;
    244         size++;
    245 
    246         sorted = sorted
    247                 && (n == 0 || value > values[n-1])
    248                 && (n == (size - 1) || value < values[n+1]);
    249     }
    250 
    251     /**
    252      * Removes an element at a given index, shifting elements at greater
    253      * indicies down one.
    254      *
    255      * @param n  {@code >=0, < size();} index of element to remove
    256      */
    257     public void removeIndex(int n) {
    258         if (n >= size) {
    259             throw new IndexOutOfBoundsException("n >= size()");
    260         }
    261 
    262         System.arraycopy (values, n + 1, values, n, size - n - 1);
    263         size--;
    264 
    265         // sort status is unchanged
    266     }
    267 
    268     /**
    269      * Increases size of array if needed
    270      */
    271     private void growIfNeeded() {
    272         if (size == values.length) {
    273             // Resize.
    274             int[] newv = new int[size * 3 / 2 + 10];
    275             System.arraycopy(values, 0, newv, 0, size);
    276             values = newv;
    277         }
    278     }
    279 
    280     /**
    281      * Returns the last element in the array without modifying the array
    282      *
    283      * @return last value in the array.
    284      * @exception IndexOutOfBoundsException if stack is empty.
    285      */
    286     public int top() {
    287         return get(size - 1);
    288     }
    289 
    290     /**
    291      * Pops an element off the end of the list and decreasing the size by one.
    292      *
    293      * @return value from what was the last element.
    294      * @exception IndexOutOfBoundsException if stack is empty.
    295      */
    296     public int pop() {
    297         throwIfImmutable();
    298 
    299         int result;
    300 
    301         result = get(size-1);
    302         size--;
    303 
    304         return result;
    305     }
    306 
    307     /**
    308      * Pops N elements off the end of the list and decreasing the size by N.
    309      *
    310      * @param n {@code >= 0;} number of elements to remove from end.
    311      * @exception IndexOutOfBoundsException if stack is smaller than N
    312      */
    313     public void pop(int n) {
    314         throwIfImmutable();
    315 
    316         size -= n;
    317     }
    318 
    319     /**
    320      * Shrinks the size of the list.
    321      *
    322      * @param newSize {@code >= 0;} the new size
    323      */
    324     public void shrink(int newSize) {
    325         if (newSize < 0) {
    326             throw new IllegalArgumentException("newSize < 0");
    327         }
    328 
    329         if (newSize > size) {
    330             throw new IllegalArgumentException("newSize > size");
    331         }
    332 
    333         throwIfImmutable();
    334 
    335         size = newSize;
    336     }
    337 
    338     /**
    339      * Makes and returns a mutable copy of the list.
    340      *
    341      * @return {@code non-null;} an appropriately-constructed instance
    342      */
    343     public IntList mutableCopy() {
    344         int sz = size;
    345         IntList result = new IntList(sz);
    346 
    347         for (int i = 0; i < sz; i++) {
    348             result.add(values[i]);
    349         }
    350 
    351         return result;
    352     }
    353 
    354     /**
    355      * Sorts the elements in the list in-place.
    356      */
    357     public void sort() {
    358         throwIfImmutable();
    359 
    360         if (!sorted) {
    361             Arrays.sort(values, 0, size);
    362             sorted = true;
    363         }
    364     }
    365 
    366     /**
    367      * Returns the index of the given value, or -1 if the value does not
    368      * appear in the list.  This will do a binary search if the list is
    369      * sorted or a linear search if not.
    370      * @param value value to find
    371      * @return index of value or -1
    372      */
    373     public int indexOf(int value) {
    374         int ret = binarysearch(value);
    375 
    376         return ret >= 0 ? ret : -1;
    377 
    378     }
    379 
    380     /**
    381      * Performs a binary search on a sorted list, returning the index of
    382      * the given value if it is present or
    383      * {@code (-(insertion point) - 1)} if the value is not present.
    384      * If the list is not sorted, then reverts to linear search and returns
    385      * {@code -size()} if the element is not found.
    386      *
    387      * @param value value to find
    388      * @return index of value or {@code (-(insertion point) - 1)} if the
    389      * value is not present
    390      */
    391     public int binarysearch(int value) {
    392         int sz = size;
    393 
    394         if (!sorted) {
    395             // Linear search.
    396             for (int i = 0; i < sz; i++) {
    397                 if (values[i] == value) {
    398                     return i;
    399                 }
    400             }
    401 
    402             return -sz;
    403         }
    404 
    405         /*
    406          * Binary search. This variant does only one value comparison
    407          * per iteration but does one more iteration on average than
    408          * the variant that includes a value equality check per
    409          * iteration.
    410          */
    411 
    412         int min = -1;
    413         int max = sz;
    414 
    415         while (max > (min + 1)) {
    416             /*
    417              * The guessIdx calculation is equivalent to ((min + max)
    418              * / 2) but won't go wonky when min and max are close to
    419              * Integer.MAX_VALUE.
    420              */
    421             int guessIdx = min + ((max - min) >> 1);
    422             int guess = values[guessIdx];
    423 
    424             if (value <= guess) {
    425                 max = guessIdx;
    426             } else {
    427                 min = guessIdx;
    428             }
    429         }
    430 
    431         if ((max != sz)) {
    432             return (value == values[max]) ? max : (-max - 1);
    433         } else {
    434             return -sz - 1;
    435         }
    436     }
    437 
    438 
    439     /**
    440      * Returns whether or not the given value appears in the list.
    441      * This will do a binary search if the list is sorted or a linear
    442      * search if not.
    443      *
    444      * @see #sort
    445      *
    446      * @param value value to look for
    447      * @return whether the list contains the given value
    448      */
    449     public boolean contains(int value) {
    450         return indexOf(value) >= 0;
    451     }
    452 }
    453