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.DoubleList; 14 import java.util.Arrays; 15 import java.util.Collection; 16 import java.util.RandomAccess; 17 18 /** 19 * An implementation of {@link DoubleList} on top of a primitive array. 20 * 21 * @author dweis@google.com (Daniel Weis) 22 */ 23 final class DoubleArrayList extends AbstractProtobufList<Double> 24 implements DoubleList, RandomAccess, PrimitiveNonBoxingCollection { 25 26 private static final double[] EMPTY_ARRAY = new double[0]; 27 28 private static final DoubleArrayList EMPTY_LIST = new DoubleArrayList(EMPTY_ARRAY, 0, false); 29 emptyList()30 public static DoubleArrayList emptyList() { 31 return EMPTY_LIST; 32 } 33 34 /** The backing store for the list. */ 35 private double[] 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 DoubleArrayList} with default capacity. */ DoubleArrayList()44 DoubleArrayList() { 45 this(EMPTY_ARRAY, 0, true); 46 } 47 48 /** 49 * Constructs a new mutable {@code DoubleArrayList} containing the same elements as {@code other}. 50 */ DoubleArrayList(double[] other, int size, boolean isMutable)51 private DoubleArrayList(double[] other, int size, boolean isMutable) { 52 super(isMutable); 53 this.array = other; 54 this.size = size; 55 } 56 57 @Override removeRange(int fromIndex, int toIndex)58 protected void removeRange(int fromIndex, int toIndex) { 59 ensureIsMutable(); 60 if (toIndex < fromIndex) { 61 throw new IndexOutOfBoundsException("toIndex < fromIndex"); 62 } 63 64 System.arraycopy(array, toIndex, array, fromIndex, size - toIndex); 65 size -= (toIndex - fromIndex); 66 modCount++; 67 } 68 69 @Override equals(Object o)70 public boolean equals(Object o) { 71 if (this == o) { 72 return true; 73 } 74 if (!(o instanceof DoubleArrayList)) { 75 return super.equals(o); 76 } 77 DoubleArrayList other = (DoubleArrayList) o; 78 if (size != other.size) { 79 return false; 80 } 81 82 final double[] arr = other.array; 83 for (int i = 0; i < size; i++) { 84 if (Double.doubleToLongBits(array[i]) != Double.doubleToLongBits(arr[i])) { 85 return false; 86 } 87 } 88 89 return true; 90 } 91 92 @Override hashCode()93 public int hashCode() { 94 int result = 1; 95 for (int i = 0; i < size; i++) { 96 long bits = Double.doubleToLongBits(array[i]); 97 result = (31 * result) + Internal.hashLong(bits); 98 } 99 return result; 100 } 101 102 @Override mutableCopyWithCapacity(int capacity)103 public DoubleList mutableCopyWithCapacity(int capacity) { 104 if (capacity < size) { 105 throw new IllegalArgumentException(); 106 } 107 double[] newArray = capacity == 0 ? EMPTY_ARRAY : Arrays.copyOf(array, capacity); 108 return new DoubleArrayList(newArray, size, true); 109 } 110 111 @Override get(int index)112 public Double get(int index) { 113 return getDouble(index); 114 } 115 116 @Override getDouble(int index)117 public double getDouble(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 Double)) { 125 return -1; 126 } 127 double unboxedElement = (Double) 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, Double element)148 public Double set(int index, Double element) { 149 return setDouble(index, element); 150 } 151 152 @Override setDouble(int index, double element)153 public double setDouble(int index, double element) { 154 ensureIsMutable(); 155 ensureIndexInRange(index); 156 double previousValue = array[index]; 157 array[index] = element; 158 return previousValue; 159 } 160 161 @Override add(Double element)162 public boolean add(Double element) { 163 addDouble(element); 164 return true; 165 } 166 167 @Override add(int index, Double element)168 public void add(int index, Double element) { 169 addDouble(index, element); 170 } 171 172 /** Like {@link #add(Double)} but more efficient in that it doesn't box the element. */ 173 @Override addDouble(double element)174 public void addDouble(double element) { 175 ensureIsMutable(); 176 if (size == array.length) { 177 int length = growSize(array.length); 178 double[] newArray = new double[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, Double)} but more efficient in that it doesn't box the element. */ addDouble(int index, double element)188 private void addDouble(int index, double 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 double[] newArray = new double[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 Double> collection)215 public boolean addAll(Collection<? extends Double> collection) { 216 ensureIsMutable(); 217 218 checkNotNull(collection); 219 220 // We specialize when adding another DoubleArrayList to avoid boxing elements. 221 if (!(collection instanceof DoubleArrayList)) { 222 return super.addAll(collection); 223 } 224 225 DoubleArrayList list = (DoubleArrayList) 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 Double remove(int index) { 249 ensureIsMutable(); 250 ensureIndexInRange(index); 251 double 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 double[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