Why there is no ArrayList(T[] t) constructor?
It is very common to initialize list by array of objects in such way:
Foo[] objs = ...;开发者_运维问答
ArrayList<Foo> list = new ArrayList<Foo>(Arrays.asList(objs));
I wonder, is there any reason why desiners of ArrayList didn't include constructor with array as parameter, so that it could be initialized like that:
ArrayList<Foo> list = new ArrayList<Foo>(objs);
May be it violates some principles, thread-safety or something else?
I don't know why it's not in the standard library, but Guava's Lists
class has newArrayList
which even helps with type inference:
ArrayList<Foo> list = Lists.newArrayList(objs);
(You may want to import Lists.newArrayList
statically if you use it a lot.)
You can use Google Guava Library (AKA Google Collections) for this:
String[] ary = {"a", "b"};
List<String> l = Lists.newArrayList(ary);
http://code.google.com/p/guava-libraries/
It seems that the reason that ArrayList does not implement any constructor itself but just delegates the call to super class constructor, i.e. constructor of AbstractList. There are a lot of list implementations including LinkedList that should not have constructor that accepts array.
I think that absence of constructor that accepts array and presence of static utility method in class Arrays is an example of good design and better code reuse. That's right that it cause us to write a little bit more verbose code, but this is the price.
BTW if you are using static import java.util.Arrays
you can then call asList()
without mentioning Arrays, so the code is not to verbose.
java.util.Arrays
internally uses its own version of ArrayList
class...
private static class ArrayList<E> extends AbstractList<E> implements RandomAccess, java.io.Serializable
{
...
}
No idea why it is not included as a standard constructor call... but you can follow the lead of java.util.Arrays
and have your own version of Arraylist.
Construction new ArrayList<Foo>(Arrays.asList(array))
makes so little sense to me, that I am completely unable to remember it.
Should have done it long ago, but better late than never.
import java.lang.reflect.Field;
import java.util.ArrayList;
public class RealArrayList<E> extends ArrayList<E>{
static final String ELEMENT_DATA_FIELD_NAME = "elementData";
static final String SIZE_FIELD_NAME = "size";
public <E> RealArrayList (E[] array){
try {
Field elementDataField = getClass().getSuperclass()
.getDeclaredField(ELEMENT_DATA_FIELD_NAME);
elementDataField.setAccessible(true);
elementDataField.set(this, array);
Field sizeField = getClass().getSuperclass()
.getDeclaredField(SIZE_FIELD_NAME);
sizeField.setAccessible(true);
sizeField.set(this, array.length);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
Works with Objects including Strings. Does not work with primitives. To make it work with primitives, probably, should be added constructors for each primitive type with the conversion of items to object wrappers.
Update. Here is the final version that works with objects and primitives. Not so heavily tested.
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
public class RealArrayList<E> extends ArrayList<E> {
static final String ELEMENT_DATA_FIELD_NAME = "elementData";
static final String SIZE_FIELD_NAME = "size";
public <E> RealArrayList(E[] array) {
setElementDataAndSize(array);
}
public RealArrayList(int[] array) {
Object[] arrayOfObjects = convertArrayOfPrimitivesToArrayOfObjects(array);
setElementDataAndSize(arrayOfObjects);
}
public RealArrayList(byte[] array) {
Object[] arrayOfObjects = convertArrayOfPrimitivesToArrayOfObjects(array);
setElementDataAndSize(arrayOfObjects);
}
public RealArrayList(char[] array) {
Object[] arrayOfObjects = convertArrayOfPrimitivesToArrayOfObjects(array);
setElementDataAndSize(arrayOfObjects);
}
public RealArrayList(short[] array) {
Object[] arrayOfObjects = convertArrayOfPrimitivesToArrayOfObjects(array);
setElementDataAndSize(arrayOfObjects);
}
public RealArrayList(long[] array) {
Object[] arrayOfObjects = convertArrayOfPrimitivesToArrayOfObjects(array);
setElementDataAndSize(arrayOfObjects);
}
public RealArrayList(float[] array) {
Object[] arrayOfObjects = convertArrayOfPrimitivesToArrayOfObjects(array);
setElementDataAndSize(arrayOfObjects);
}
public RealArrayList(double[] array) {
Object[] arrayOfObjects = convertArrayOfPrimitivesToArrayOfObjects(array);
setElementDataAndSize(arrayOfObjects);
}
public RealArrayList(boolean[] array) {
Object[] arrayOfObjects = convertArrayOfPrimitivesToArrayOfObjects(array);
setElementDataAndSize(arrayOfObjects);
}
private <E> void setElementDataAndSize(E[] array) {
getElementDataFieldAndSetValue(array);
getSizeFieldAndSetValue(array.length);
}
private <E> void getElementDataFieldAndSetValue(E[] array) {
try {
Field field = getClass().getSuperclass().getDeclaredField(ELEMENT_DATA_FIELD_NAME);
field.setAccessible(true);
field.set(this, array);
} catch (Exception ex) {
Logger.getLogger(RealArrayList.class.getName()).log(Level.SEVERE, null, ex);
}
}
private void getSizeFieldAndSetValue(int size) {
try {
Field field = getClass().getSuperclass().getDeclaredField(SIZE_FIELD_NAME);
field.setAccessible(true);
field.set(this, size);
} catch (Exception ex) {
Logger.getLogger(RealArrayList.class.getName()).log(Level.SEVERE, null, ex);
}
}
private Object[] convertArrayOfPrimitivesToArrayOfObjects(Object array) {
int arraylength = Array.getLength(array);
Object[] outputArray = new Object[arraylength];
for (int i = 0; i < arraylength; ++i) {
outputArray[i] = Array.get(array, i);
}
return outputArray;
}
}
The only problem I found so far is that realArrayList.remove('a')
throws IndexOutOfBoundsException
. Somehow ArrayList.remove('a')
treats 'a' as integer rather than character. Workaround is to use remove(new Character('a'))
.
精彩评论