When I was going through ArrayList implementation, I found a weird piece of code in toArray(T[]) method.
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
The part is,
if (a.length > size)
a[size] = null;
why only the element at this index in the array is set to null? Once the array is filled with the contents of the list, the elements at the remaining indices should have been set to null, right? Or am I missing something here?
The javadoc explains why:
If the list fits in the specified array with room to spare (i.e., the array has more elements than the list), the element in the array immediately following the end of the list is set to null. (This is useful in determining the length of the list only if the caller knows that the list does not contain any null elements.)
Related
ArrayList<Integer>arr1=new ArrayList<Integer>();
arr1.add(1);
arr1.add(2);
arr1.add(3);
ArrayList<Integer>copyArray=new ArrayList<Integer>(arr1);
Does the above code to replicate one arrayList to another arrayList work in O(1) or deep down it's O(n) ,as seems that to copy each element of one array to another it would iterate one by one which would result in O(n) ,or is it just a reference to a object pool ?
Looking to the source:
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
size = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
The ArrayList(Collection) constructor calls toArray() which calls Arrays.copyOf:
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
#SuppressWarnings("unchecked")
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
The important part here is System.arraycopy which is a native call. Another question on SO addresses the time complexity of this call, and the conclusion seems to be that this call is O(n) in worst case scenarios but that some systems may use block copies for some data types (like primitives I imagine), thus resulting in more efficient runtime than for loop iteration.
Internally it uses Arrays.copyOf which further uses System.arraycopy whose complexity is O(n). Checkout the source below:
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
Checking the source code , ArrayList(Collection<? extends E> c) constructor internally calls c.to Array()
The Java Doc for toArray() https://docs.oracle.com/javase/9/docs/api/java/util/ArrayList.html#toArray--
says its does a deep copy of the source array and returns the safe copied array. So any change the client makes to the retuned array doesn't impact the internal datastructure of the ArrayList. So in worst case its O(n).
I recently had a problem typecasting/converting an ArrayList of arrays to a 2d array. I found an answer on this site that told me to use
List<Object[]> arrayList;
// initialize and fill the list of arrays
// all arrays have the same length
Object[][] array2d = arrayList.toArray(new Object[][] {});
That worked, but I googled it for a bit and read somewhere that it is conseidered bad practice. I still used it, as it was the only one line variant that actually worked.
What does it actually mean and why is it considered bad? I don't understand the [][]{} bit. I'm guessing that you pass an empty but initialized Object[][] to the .toArray() method?
The only thing I see is that it creates an array but will not use it.
One the Javadoc you can see:
Returns an array containing all of the elements in this list in proper sequence (from first to last element); the runtime type of the returned array is that of the specified array. If the list fits in the specified array, it is returned therein. Otherwise, a new array is allocated with the runtime type of the specified array and the size of this list.
And it can be verified on the method source code:
#SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
So what you are creating is an empty array, then the method will create another one.
Object[][] array2d = arrayList.toArray(new Object[arrayList.size()][]);
Concerning the [][]{} your guess is correct.
Some tips
here
I can't find any reason for your example be considered bad practice!
The question is pretty much as stated in the title. I'm in an algorithms course and the professor and I disagree regarding whether or not operations performed on an ArrayList sublist (a sublist generated by ArrayList.sublist) can be considered 'in place'. To my read of the Java API:
Returns a view of the portion of this list between the specified fromIndex, inclusive, and toIndex, exclusive. (If fromIndex and toIndex are equal, the returned list is empty.) The returned list is backed by this list, so non-structural changes in the returned list are reflected in this list, and vice-versa. The returned list supports all of the optional list operations.
you are still manipulating the 'master' ArrayList directly. To his view, you are copying references from the 'master' array into a new sub-array which means employing ArrayList.subList is not considered 'in place'. Obviously, for the purposes of the course what he says goes (that is, if I want to pass :-/) but I would like to know either way for my own growth as a programmer. Code is below - and thank you!
public static int findK (int findME, int mVal, ArrayList<Integer> arr) {
// pre stage return variable
int returnVal = -1;
// make a subarray consisting of indexes 0 - ((m-2)+(m-1)).
// this because the relationship between the m, p, and q
// is p>q>m - therfore the max value of q is m-1 and the
// max value of p is m-2.
int newArrSize = (mVal-2) + (mVal-1);
ArrayList<Integer> subArr = new ArrayList<Integer>(arr.subList(0, newArrSize));
// make the list smaller by looking at only the last [mVal]
// elements. this because we know what we're looking for
// has to be in the second section of the array, and that
// section can't possibly be larger than mVal
int fromIndex = subArr.size() - mVal;
subArr = new ArrayList<Integer> (subArr.subList(fromIndex, subArr.size()));
// at this point we can do a simple binary search, which on an
// a sorted array of size mVal is lg(m)
while (subArr.size() > 1) {
// get midpoint value
int midPointIndex = subArr.size() / 2;
int midPointValue = subArr.get(midPointIndex);
// check for case where midpoint value is in the first
// region of the array
// check for case where the midpoint is less than the
// findME value
//
// if true, discard first half of the array
if ((midPointValue == 9000) || (midPointValue < findME)) {
subArr = new ArrayList<Integer> (subArr.subList(midPointIndex, subArr.size()));
continue;
}
// else if midpoint is less than findMe, discard the second
// half of the array
else if (midPointValue > findME) {
subArr = new ArrayList<Integer> (subArr.subList(0, midPointIndex));
continue;
}
// if we're here, we've found our value!
returnVal = midPointValue;
break;
}
// check for match and return result to caller
// only perform check if we haven't already found the value
// we're looking for
if (returnVal == -1) returnVal = (subArr.get(0) == findME) ? (subArr.get(0)) : (-1);
return returnVal;
}
I assume in this answer, that by "in place" actually "uses constant additional memory" is meant.
The sublist function creates a view of the original list. This uses only O(1) memory.
However you allocate a new list (Indices were replaced with my own names here, for simplicity):
subArr = new ArrayList<Integer> (subArr.subList(index1, index2));
What you do with such a statement is:
create a subList view (uses O(1) memory)
copy the sublist (uses O(sublist size) = O(index2 - index1) memory).
delete reference to subList (and by that the reference to the old list too)
Note that the garbage collector can not claim the memory of the old list until all references to it are deleted. The sublist view contains a reference to the old list, so the old list cannot be claimed by the GC until all references to the sublist view are deleted. This means for a short while you use O(index2 - index1) more memory than in the list at the beginning. Since binary search makes the list half as large in every step, you use O(subArr.size()) additional memory (not in O(1)).
Lines like these:
subArr = new ArrayList<Integer> (subArr.subList(fromIndex, subArr.size()));
That's your "copy". The new ArrayList is indeed, a copy of the data from the subList.
If you were using the subList "raw", then it could be better argued that you are "in place", because then the subList is simply a set of offsets in to the original array.
But with the create of new ArrayLists, you are definitely copying.
Of course, the easiest way to check is that when you find your value, change the value in the list to some sentinel (9999 or whatever). Then dump your original list. If 9999 shows up, it's in place. If not, then, it's not.
"In-place" isn't a term that applies to binary-search, as it almost always refers to how modifications are made (e.g. for sorting, like quicksort (in-place) and mergesort (not in-place)). So it's not something you need to worry about for binary search, as searching makes no modifications.
As for whether ArrayList#subList() copies data, a look at the source code should prove that that is incorrect. Unfortunately, the source is a tad long for a SO answer, but I'll do my best to summarize.
The subList() method is this:
public List<E> subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);
return new SubList(this, 0, fromIndex, toIndex);
}
Where SubList is defined as the inner class:
private class SubList extends AbstractList<E> implements RandomAccess
with instance fields
private final AbstractList<E> parent;
private final int parentOffset;
private final int offset;
int size;
Note how none of those fields are a type of array or list.
Looking at the implementations of mutators shows that all work is delegated to the parent class. For example, here is set():
public E set(int index, E e) {
rangeCheck(index);
checkForComodification();
E oldValue = ArrayList.this.elementData(offset + index);
ArrayList.this.elementData[offset + index] = e;
return oldValue;
}
Notice how ArrayList.this is used to refer to the containing ArrayList instance, which means that the source ArrayList implementation is modified and not any (nonexistent) copy.
The add() method shows something similar:
public void add(int index, E e) {
rangeCheckForAdd(index);
checkForComodification();
parent.add(parentOffset + index, e);
this.modCount = parent.modCount;
this.size++;
}
parent.add() is used here, where parent is also the containing instance. So again, it is the source list that is modified, and not any (nonexistent) copy.
And so on and so forth.
However, as pointed out by Will Hartung, all this is moot if you pass the resulting SubList into the constructor of a new ArrayList<>(), as the constructor:
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray(); // <------------ This line
size = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
makes a copy of the internal array (through toArray()), which is the copy your professor/TA were likely talking about.
I am trying to convert a Set to an Array.
Set<String> s = new HashSet<String>(Arrays.asList("mango","guava","apple"));
String[] a = s.toArray(new String[0]);
for(String x:a)
System.out.println(x);
And it works fine. But I don't understand the significance of new String[0] in String[] a = s.toArray(new String[0]);.
I mean initially I was trying String[] a = c.toArray();, but it wan't working. Why is the need for new String[0].
It is the array into which the elements of the Set are to be stored, if it is big enough; otherwise, a new array of the same runtime type is allocated for this purpose.
Object[] toArray(), returns an Object[] which cannot be cast to String[] or any other type array.
T[] toArray(T[] a) , returns an array containing all of the elements in this set; the runtime type of the returned array is that of the specified array. If the set fits in the specified array, it is returned therein. Otherwise, a new array is allocated with the runtime type of the specified array and the size of this set.
If you go through the implementing code (I'm posting the code from OpenJDK) , it will be clear for you :
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
The parameter is a result of one of the many well-known limitations in the Java generics system. Basically, the parameter is needed in order to be able to return an array of the correct type.
In the Java collections framework, the Collection interface declares the following method:
<T> T[] toArray(T[] a)
Returns an array containing all of the elements in this collection; the runtime type of the returned array is that of the specified array. If the collection fits in the specified array, it is returned therein. Otherwise, a new array is allocated with the runtime type of the specified array and the size of this collection.
If you wanted to implement this method, how would you create an array of the type of a, known only at runtime?
Use the static method
java.lang.reflect.Array.newInstance(Class<?> componentType, int length)
A tutorial on its use can be found here:
http://java.sun.com/docs/books/tutorial/reflect/special/arrayInstance.html
By looking at how ArrayList does it:
public <T> T[] toArray(T[] a) {
if (a.length < size)
a = (T[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
Array.newInstance(Class componentType, int length)
To create a new array of a generic type (which is only known at runtime), you have to create an array of Objects and simply cast it to the generic type and then use it as such. This is a limitation of the generics implementation of Java (erasure).
T[] newArray = (T[]) new Object[X]; // where X is the number of elements you want.
The function then takes the array given (a) and uses it (checking it's size beforehand) or creates a new one.