1 // Protocol Buffers - Google's data interchange format 2 // Copyright 2008 Google Inc. All rights reserved. 3 // 4 // Use of this source code is governed by a BSD-style 5 // license that can be found in the LICENSE file or at 6 // https://developers.google.com/open-source/licenses/bsd 7 8 package com.google.protobuf; 9 10 import static com.google.protobuf.Internal.checkNotNull; 11 import static java.lang.Math.max; 12 13 import com.google.protobuf.Internal.BooleanList; 14 import java.util.Arrays; 15 import java.util.Collection; 16 import java.util.RandomAccess; 17 18 /** 19 * An implementation of {@link BooleanList} on top of a primitive array. 20 * 21 * @author dweis@google.com (Daniel Weis) 22 */ 23 final class BooleanArrayList extends AbstractProtobufList<Boolean> 24 implements BooleanList, RandomAccess, PrimitiveNonBoxingCollection { 25 26 private static final boolean[] EMPTY_ARRAY = new boolean[0]; 27 28 private static final BooleanArrayList EMPTY_LIST = new BooleanArrayList(EMPTY_ARRAY, 0, false); 29 emptyList()30 public static BooleanArrayList emptyList() { 31 return EMPTY_LIST; 32 } 33 34 /** The backing store for the list. */ 35 private boolean[] array; 36 37 /** 38 * The size of the list distinct from the length of the array. That is, it is the number of 39 * elements set in the list. 40 */ 41 private int size; 42 43 /** Constructs a new mutable {@code BooleanArrayList} with default capacity. */ BooleanArrayList()44 BooleanArrayList() { 45 this(EMPTY_ARRAY, 0, true); 46 } 47 48 /** 49 * Constructs a new mutable {@code BooleanArrayList} containing the same elements as {@code 50 * other}. 51 */ BooleanArrayList(boolean[] other, int size, boolean isMutable)52 private BooleanArrayList(boolean[] other, int size, boolean isMutable) { 53 super(isMutable); 54 this.array = other; 55 this.size = size; 56 } 57 58 @Override removeRange(int fromIndex, int toIndex)59 protected void removeRange(int fromIndex, int toIndex) { 60 ensureIsMutable(); 61 if (toIndex < fromIndex) { 62 throw new IndexOutOfBoundsException("toIndex < fromIndex"); 63 } 64 65 System.arraycopy(array, toIndex, array, fromIndex, size - toIndex); 66 size -= (toIndex - fromIndex); 67 modCount++; 68 } 69 70 @Override equals(Object o)71 public boolean equals(Object o) { 72 if (this == o) { 73 return true; 74 } 75 if (!(o instanceof BooleanArrayList)) { 76 return super.equals(o); 77 } 78 BooleanArrayList other = (BooleanArrayList) o; 79 if (size != other.size) { 80 return false; 81 } 82 83 final boolean[] arr = other.array; 84 for (int i = 0; i < size; i++) { 85 if (array[i] != arr[i]) { 86 return false; 87 } 88 } 89 90 return true; 91 } 92 93 @Override hashCode()94 public int hashCode() { 95 int result = 1; 96 for (int i = 0; i < size; i++) { 97 result = (31 * result) + Internal.hashBoolean(array[i]); 98 } 99 return result; 100 } 101 102 @Override mutableCopyWithCapacity(int capacity)103 public BooleanList mutableCopyWithCapacity(int capacity) { 104 if (capacity < size) { 105 throw new IllegalArgumentException(); 106 } 107 boolean[] newArray = capacity == 0 ? EMPTY_ARRAY : Arrays.copyOf(array, capacity); 108 return new BooleanArrayList(newArray, size, true); 109 } 110 111 @Override get(int index)112 public Boolean get(int index) { 113 return getBoolean(index); 114 } 115 116 @Override getBoolean(int index)117 public boolean getBoolean(int index) { 118 ensureIndexInRange(index); 119 return array[index]; 120 } 121 122 @Override indexOf(Object element)123 public int indexOf(Object element) { 124 if (!(element instanceof Boolean)) { 125 return -1; 126 } 127 boolean unboxedElement = (Boolean) element; 128 int numElems = size(); 129 for (int i = 0; i < numElems; i++) { 130 if (array[i] == unboxedElement) { 131 return i; 132 } 133 } 134 return -1; 135 } 136 137 @Override contains(Object element)138 public boolean contains(Object element) { 139 return indexOf(element) != -1; 140 } 141 142 @Override size()143 public int size() { 144 return size; 145 } 146 147 @Override set(int index, Boolean element)148 public Boolean set(int index, Boolean element) { 149 return setBoolean(index, element); 150 } 151 152 @Override setBoolean(int index, boolean element)153 public boolean setBoolean(int index, boolean element) { 154 ensureIsMutable(); 155 ensureIndexInRange(index); 156 boolean previousValue = array[index]; 157 array[index] = element; 158 return previousValue; 159 } 160 161 @Override add(Boolean element)162 public boolean add(Boolean element) { 163 addBoolean(element); 164 return true; 165 } 166 167 @Override add(int index, Boolean element)168 public void add(int index, Boolean element) { 169 addBoolean(index, element); 170 } 171 172 /** Like {@link #add(Boolean)} but more efficient in that it doesn't box the element. */ 173 @Override addBoolean(boolean element)174 public void addBoolean(boolean element) { 175 ensureIsMutable(); 176 if (size == array.length) { 177 int length = growSize(array.length); 178 boolean[] newArray = new boolean[length]; 179 180 System.arraycopy(array, 0, newArray, 0, size); 181 array = newArray; 182 } 183 184 array[size++] = element; 185 } 186 187 /** Like {@link #add(int, Boolean)} but more efficient in that it doesn't box the element. */ addBoolean(int index, boolean element)188 private void addBoolean(int index, boolean element) { 189 ensureIsMutable(); 190 if (index < 0 || index > size) { 191 throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index)); 192 } 193 194 if (size < array.length) { 195 // Shift everything over to make room 196 System.arraycopy(array, index, array, index + 1, size - index); 197 } else { 198 int length = growSize(array.length); 199 boolean[] newArray = new boolean[length]; 200 201 // Copy the first part directly 202 System.arraycopy(array, 0, newArray, 0, index); 203 204 // Copy the rest shifted over by one to make room 205 System.arraycopy(array, index, newArray, index + 1, size - index); 206 array = newArray; 207 } 208 209 array[index] = element; 210 size++; 211 modCount++; 212 } 213 214 @Override addAll(Collection<? extends Boolean> collection)215 public boolean addAll(Collection<? extends Boolean> collection) { 216 ensureIsMutable(); 217 218 checkNotNull(collection); 219 220 // We specialize when adding another BooleanArrayList to avoid boxing elements. 221 if (!(collection instanceof BooleanArrayList)) { 222 return super.addAll(collection); 223 } 224 225 BooleanArrayList list = (BooleanArrayList) collection; 226 if (list.size == 0) { 227 return false; 228 } 229 230 int overflow = Integer.MAX_VALUE - size; 231 if (overflow < list.size) { 232 // We can't actually represent a list this large. 233 throw new OutOfMemoryError(); 234 } 235 236 int newSize = size + list.size; 237 if (newSize > array.length) { 238 array = Arrays.copyOf(array, newSize); 239 } 240 241 System.arraycopy(list.array, 0, array, size, list.size); 242 size = newSize; 243 modCount++; 244 return true; 245 } 246 247 @Override remove(int index)248 public Boolean remove(int index) { 249 ensureIsMutable(); 250 ensureIndexInRange(index); 251 boolean value = array[index]; 252 if (index < size - 1) { 253 System.arraycopy(array, index + 1, array, index, size - index - 1); 254 } 255 size--; 256 modCount++; 257 return value; 258 } 259 260 /** Ensures the backing array can fit at least minCapacity elements. */ ensureCapacity(int minCapacity)261 void ensureCapacity(int minCapacity) { 262 if (minCapacity <= array.length) { 263 return; 264 } 265 if (array.length == 0) { 266 array = new boolean[max(minCapacity, DEFAULT_CAPACITY)]; 267 return; 268 } 269 // To avoid quadratic copying when calling .addAllFoo(List) in a loop, we must not size to 270 // exactly the requested capacity, but must exponentially grow instead. This is similar 271 // behaviour to ArrayList. 272 int n = array.length; 273 while (n < minCapacity) { 274 n = growSize(n); 275 } 276 array = Arrays.copyOf(array, n); 277 } 278 growSize(int previousSize)279 private static int growSize(int previousSize) { 280 // Resize to 1.5x the size, rounding up to DEFAULT_CAPACITY. 281 return max(((previousSize * 3) / 2) + 1, DEFAULT_CAPACITY); 282 } 283 284 /** 285 * Ensures that the provided {@code index} is within the range of {@code [0, size]}. Throws an 286 * {@link IndexOutOfBoundsException} if it is not. 287 * 288 * @param index the index to verify is in range 289 */ ensureIndexInRange(int index)290 private void ensureIndexInRange(int index) { 291 if (index < 0 || index >= size) { 292 throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index)); 293 } 294 } 295 makeOutOfBoundsExceptionMessage(int index)296 private String makeOutOfBoundsExceptionMessage(int index) { 297 return "Index:" + index + ", Size:" + size; 298 } 299 } 300