001 // Copyright 2006-08 Regents of the University of California. May be used 002 // under the terms of the revised BSD license. See LICENSING for details. 003 /** 004 * @author Adrian Mettler 005 */ 006 package org.joe_e.array; 007 008 import java.io.IOException; 009 import java.io.ObjectInputStream; 010 import java.io.ObjectOutputStream; 011 import java.util.Arrays; 012 import java.lang.reflect.Array; 013 014 /** 015 * An immutable array of <code>long</code>. 016 */ 017 public final class LongArray extends PowerlessArray<Long> { 018 static private final long serialVersionUID = 1L; 019 020 private /* final */ transient long[] longs; 021 022 LongArray(long... longs) { 023 // Use back door constructor that sets backing store to null. 024 // This lets ConstArray's methods know not to use the backing 025 // store for accessing this object. 026 super(null); 027 this.longs = longs; 028 } 029 030 /** 031 * Constructs an array of <code>long</code>s. 032 * @param longs each element 033 */ 034 static public LongArray array(final long... longs) { 035 return new LongArray(longs.clone()); 036 } 037 038 /* 039 * The following are necessary because otherwise calls with <=4 arguments 040 * are resolved to the superclass PowerlessArray 041 */ 042 043 /** 044 * Construct an empty <code>LongArray</code> 045 */ 046 @SuppressWarnings("unchecked") // the warning here seems completely bogus 047 static public LongArray array() { 048 return new LongArray(new long[]{}); 049 } 050 051 /** 052 * Construct a <code>LongArray</code> with one element. 053 * @param value the value 054 */ 055 static public LongArray array(long value) { 056 return new LongArray(new long[]{value}); 057 } 058 059 /** 060 * Construct a <code>LongArray</code> with two elements. 061 * @param value1 the first value 062 * @param value2 the second value 063 */ 064 static public LongArray array(long value1, long value2) { 065 return new LongArray(new long[]{value1, value2}); 066 } 067 068 /** 069 * Construct a <code>LongArray</code> with three elements. 070 * @param value1 the first value 071 * @param value2 the second value 072 * @param value3 the third value 073 */ 074 static public LongArray array(long value1, long value2, long value3) { 075 return new LongArray(new long[]{value1, value2, value3}); 076 } 077 078 /** 079 * Construct a <code>LongArray</code> with four elements. 080 * @param value1 the first value 081 * @param value2 the second value 082 * @param value3 the third value 083 * @param value4 the fourth value 084 */ 085 static public LongArray array(long value1, long value2, long value3, 086 long value4) { 087 return new LongArray(new long[]{value1, value2, value3, value4}); 088 } 089 090 // java.io.Serializable interface 091 092 /* 093 * Serialization hacks to prevent the contents from being serialized as a 094 * mutable array. This improves efficiency for projects that serialize 095 * Joe-E objects using Java's serialization API by avoiding treatment of 096 * immutable state as mutable. These methods can otherwise be ignored. 097 */ 098 private void writeObject(final ObjectOutputStream out) throws IOException { 099 out.defaultWriteObject(); 100 101 out.writeInt(longs.length); 102 for (long c : longs) { 103 out.writeLong(c); 104 } 105 } 106 107 private void readObject(final ObjectInputStream in) throws 108 IOException, ClassNotFoundException { 109 in.defaultReadObject(); 110 111 final int length = in.readInt(); 112 longs = new long[length]; 113 for (int i = 0; i < length; ++i) { 114 longs[i] = in.readLong(); 115 } 116 } 117 118 /* 119 * Methods that must be overriden, as the implementation in ConstArray 120 * would try to use arr, which is null. 121 */ 122 123 // java.lang.Object interface 124 125 /** 126 * Test for equality with another object 127 * @return true if the other object is a {@link ConstArray} with the same 128 * contents as this array 129 */ 130 public boolean equals(final Object other) { 131 if (other instanceof LongArray) { 132 // Simple case: just compare longArr fields 133 return Arrays.equals(longs, ((LongArray)other).longs); 134 } else if (other instanceof ConstArray<?>) { 135 // Other array does not have contents in longArr: 136 // check that length matches, and then compare elements one-by-one 137 final ConstArray<?> otherArray = (ConstArray<?>)other; 138 if (otherArray.length() != longs.length) { 139 return false; 140 } 141 for (int i = 0; i < longs.length; ++i) { 142 final Object otherElement = otherArray.get(i); 143 if (!(otherElement instanceof Long) || 144 ((Long)otherElement).longValue() != longs[i]) { 145 return false; 146 } 147 } 148 return true; 149 } else { 150 // Only a ConstArray can be equal to a LongArray 151 return false; 152 } 153 } 154 155 /** 156 * Computes a digest of the array for hashing. The hash code is the same 157 * as {@link java.util.Arrays#hashCode(Object[])} called on a Java array 158 * containing the same elements. 159 * @return a hash code based on the contents of this array 160 */ 161 public int hashCode() { 162 // Because wrappers for primitive types return the same hashCode as 163 // their primitive values, a LongArray has the same hashCode as a 164 // ConstArray<Long> with the same contents. 165 return Arrays.hashCode(longs); 166 } 167 168 /** 169 * Return a string representation of the array 170 */ 171 public String toString() { 172 return Arrays.toString(longs); 173 } 174 175 // org.joe_e.ConstArray interface 176 177 /** 178 * Gets the length of the array. 179 */ 180 public int length() { 181 return longs.length; 182 } 183 184 /** 185 * Creates a <code>Long</code> for a specified <code>long</code>. 186 * @param i position of the element to return 187 * @throws ArrayIndexOutOfBoundsException <code>i</code> is out of bounds 188 */ 189 public Long get(int i) { 190 return longs[i]; 191 } 192 193 /** 194 * Return a mutable copy of the array 195 * @param prototype prototype of the array to copy into 196 * @return an array containing the contents of this <code>ConstArray</code> 197 * of the same type as <code>prototype</code> 198 * @throws ArrayStoreException if an element cannot be stored in the array 199 */ 200 @SuppressWarnings("unchecked") 201 public <T> T[] toArray(T[] prototype) { 202 final int len = length(); 203 if (prototype.length < len) { 204 final Class<?> t = prototype.getClass().getComponentType(); 205 prototype = (T[])Array.newInstance(t, len); 206 } 207 208 for (int i = 0; i < len; ++i) { 209 prototype[i] = (T) (Long) longs[i]; 210 } 211 return prototype; 212 } 213 214 /** 215 * Creates a <code>LongArray<code> with an appended <code>Long</code>. 216 * @param newLong the element to append 217 * @throws NullPointerException <code>newLong</code> is null 218 */ 219 public LongArray with(final Long newLong) { 220 return with(newLong.longValue()); 221 } 222 223 /* 224 * Convenience (more efficient) methods with long 225 */ 226 227 /** 228 * Gets the <code>long</code> at a specified position. 229 * @param i position of the element to return 230 * @throws ArrayIndexOutOfBoundsException <code>i</code> is out of bounds 231 */ 232 public long getLong(final int i) { 233 return longs[i]; 234 } 235 236 /** 237 * Creates a mutable copy of the <code>long</code> array 238 */ 239 public long[] toLongArray() { 240 return longs.clone(); 241 } 242 243 /** 244 * Creates a <code>LongArray</code> with an appended <code>long</code>. 245 * @param newLong the element to append 246 */ 247 public LongArray with(final long newLong) { 248 final long[] newLongs = new long[longs.length + 1]; 249 System.arraycopy(longs, 0, newLongs, 0, longs.length); 250 newLongs[longs.length] = newLong; 251 return new LongArray(newLongs); 252 } 253 254 /** 255 * Return a new <code>LongArray</code> that contains the same elements 256 * as this one excluding the element at a specified index 257 * @param i the index of the element to exclude 258 * @return the new array 259 */ 260 public LongArray without(final int i) { 261 final long[] newArr = new long[longs.length - 1]; 262 System.arraycopy(longs, 0, newArr, 0, i); 263 System.arraycopy(longs, i + 1, newArr, i, newArr.length - i); 264 return new LongArray(newArr); 265 } 266 267 /** 268 * A {@link LongArray} factory. 269 */ 270 public static final class Builder extends 271 PowerlessArray.Builder<Long> { 272 private long[] longBuffer; 273 274 /** 275 * Construct an instance with the default internal array length. 276 */ 277 Builder() { 278 this(0); 279 } 280 281 /** 282 * Construct an instance. 283 * @param estimate estimated array length 284 */ 285 Builder(int estimate) { 286 longBuffer = new long[estimate > 0 ? estimate : 32]; 287 size = 0; 288 } 289 290 // ArrayBuilder<Long> interface 291 /** 292 * Append a <code>Long</code> 293 * @param newLong the element to add 294 * @throws NegativeArraySizeException if the resulting internal array 295 * would exceed the maximum length of a Java array. The builder is 296 * unmodified. 297 */ 298 public void append(Long newLong) { 299 append ((long) newLong); 300 } 301 302 /** 303 * Append an array of <code>Long</code>s 304 * @param newLongs the elements to add 305 * @throws IndexOutOfBoundsException if the resulting internal array 306 * would exceed the maximum length of a Java array. The builder is 307 * unmodified. 308 */ 309 public void append(final Long[] newLongs) { 310 append(newLongs, 0, newLongs.length); 311 } 312 313 /** 314 * Append a range of elements from an array of <code>Long</code>s 315 * @param newLongs the array to add elements from 316 * @param off the index of the first element to add 317 * @param len the number of elements to add 318 * @throws IndexOutOfBoundsException if an out-of-bounds index would 319 * be referenced or the resulting internal array would exceed the 320 * maximum length of a Java array. The builder is unmodified. 321 */ 322 public void append(final Long[] newLongs, 323 final int off, final int len) { 324 int newSize = size + len; 325 if (newSize < 0 || off < 0 || len < 0 || off + len < 0 326 || off + len > newLongs.length) { 327 throw new IndexOutOfBoundsException(); 328 } 329 if (newSize > longBuffer.length) { 330 int newLength = Math.max(newSize, 2 * longBuffer.length); 331 System.arraycopy(longBuffer, 0, 332 longBuffer = new long[newLength], 0, size); 333 } 334 335 for (int i = 0; i < len; ++i) { 336 longBuffer[size + i] = newLongs[off + i]; 337 } 338 size = newSize; 339 } 340 341 /** 342 * Create a snapshot of the current content. 343 * @return a <code>LongArray</code> containing the elements so far 344 */ 345 public LongArray snapshot() { 346 final long[] arr; 347 if (size == longBuffer.length) { 348 arr = longBuffer; 349 } else { 350 arr = new long[size]; 351 System.arraycopy(longBuffer, 0, arr, 0, size); 352 } 353 return new LongArray(arr); 354 } 355 356 /* 357 * Convenience (more efficient) methods with long 358 */ 359 /** 360 * Append a <code>long</code> 361 * @param newLong the element to add 362 * @throws NegativeArraySizeException if the resulting internal array 363 * would exceed the maximum length of a Java array. The builder is 364 * unmodified. 365 */ 366 public void append(final long newLong) { 367 if (size == longBuffer.length) { 368 System.arraycopy(longBuffer, 0, 369 longBuffer = new long[2 * size], 0, size); 370 } 371 longBuffer[size++] = newLong; 372 } 373 374 /** 375 * Append an array of <code>long</code>s 376 * @param newLongs the elements to add 377 * @throws IndexOutOfBoundsException if the resulting internal array 378 * would exceed the maximum length of a Java array. The builder is 379 * unmodified. 380 */ 381 public void append(final long[] newLongs) { 382 append(newLongs, 0, newLongs.length); 383 } 384 385 /** 386 * Append a range of elements from an array of <code>long</code>s 387 * @param newLongs the array to add elements from 388 * @param off the index of the first element to add 389 * @param len the number of elements to add 390 * @throws IndexOutOfBoundsException if an out-of-bounds index would 391 * be referenced or the resulting internal array would exceed the 392 * maximum length of a Java array. The builder is unmodified. 393 */ 394 public void append(final long[] newLongs, final int off, 395 final int len) { 396 int newSize = size + len; 397 if (newSize < 0 || off < 0 || len < 0 || off + len < 0 398 || off + len > newLongs.length) { 399 throw new IndexOutOfBoundsException(); 400 } 401 if (newSize > longBuffer.length) { 402 int newLength = Math.max(newSize, 2 * longBuffer.length); 403 System.arraycopy(longBuffer, 0, 404 longBuffer = new long[newLength], 0, size); 405 } 406 System.arraycopy(newLongs, off, longBuffer, size, len); 407 size = newSize; 408 } 409 } 410 411 /* If one only invokes static methods statically, this is sound, since 412 * ByteArray extends PowerlessArray<Byte> and thus this method is 413 * only required to return something of a type covariant with 414 * PowerlessArray.Builder<Byte>. Unfortunately, this is not completely 415 * sound because it is possible to invoke static methods on instances, e.g. 416 * ConstArray.Builder<String> = (ConstArray (LongArray.array())).builder() 417 * Invocations of append() can then throw ClassCastExceptions. 418 * 419 * I can't see a way to avoid this other than to de-genericize everything. 420 */ 421 422 /** 423 * Get a <code>LongArray.Builder</code>. This is equivalent to the 424 * constructor. 425 * @return a new builder instance, with the default internal array length 426 */ 427 @SuppressWarnings("unchecked") 428 public static Builder builder() { 429 return new Builder(0); 430 } 431 432 /** 433 * Get a <code>LongArray.Builder</code>. This is equivalent to the 434 * constructor. 435 * @param estimate estimated array length 436 * @return a new builder instance 437 */ 438 @SuppressWarnings("unchecked") 439 public static Builder builder(final int estimate) { 440 return new Builder(estimate); 441 } 442 }