Pass zero-sized array, save allocation? - java

In this code sample from page 114 of The Well-Grounded Java Developer, the last line:
Update[] updates = lu.toArray(new Update[0]);
contains the note: Pass zero-sized array, save allocation
List<Update> lu = new ArrayList<Update>();
String text = "";
final Update.Builder ub = new Update.Builder();
final Author a = new Author("Tallulah");
for (int i=0; i<256; i++) {
text = text + "X";
long now = System.currentTimeMillis();
lu.add(ub.author(a).updateText(text).createTime(now).build());
try {
Thread.sleep(1);
} catch (InterruptedException e) {
}
}
Collections.shuffle(lu);
Update[] updates = lu.toArray(new Update[0]);
What allocation is this saving, exactly?
The javadoc for List#toArray(T[] a) mentions:
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.
Which is what I remembered: if the array you pass to toArray(T[] a) can't fit everything in the list, a new array is allocated. Plainly, there are 256 elements in the list, which cannot fit in an array of size 0, therefore a new array must be allocated inside the method, right?
So is that note incorrect? Or is there something else it means?

Plainly, there are 256 elements in the list, which cannot fit in an array of size 0, therefore a new array must be allocated inside the method, right?
yes.
You can use
private static final Update NO_UPDATES = { }
lu.toArray(NO_UPDATES);
however this will should only help if you expect the list to be typically 0 length.
Generally, I would the same approach as fge
lu.toArray(new Update[lu.size()]);
In your specific case you know the size in advance so you can do
Update[] updates = new Update[256];
String text = "";
final Update.Builder ub = new Update.Builder();
final Author a = new Author("Tallulah");
long now = System.currentTimeMillis();
for (int i=0; i<updates.length; i++)
updates[i] = ub.author(a).updateText(text += 'X').createTime(now++).build();
Collections.shuffle(Arrays.asList(updates));

Going off of #Andreas comment on the question, I think it is a typo, and should say:
Pass zero-sized array, safe allocation.
Because if you passed nothing to the method, you'll end up calling the List#toArray() no-argument overload!
This would return an Object[] (though it would contain nothing but Update instances) and would require changing the type of the updates variable, so the last line would become:
Object[] updates = lu.toArray();
And then every time you wanted to iterate over and use the elements in that array, you'd have to cast them to Update.
Supplying the array calls the List#toArray(T[] a) method, which returns a <T> T[]. This array is reified to know it is an array of Update instances.
So supplying an empty array of Updates results in an Update[] coming back from the toArray call, not an Object[]. This is a much more type-safe allocation! The word "save" in the note must be a typo!
...this consumed way too much mental effort. Will post link to this in the book's forums so they can correct it.

It saves allocation, comparing to toArray(new Update[255]) or toArray(new Update[1000])

Related

How does the method assigns the array?

I am confused about how the array was assigned to any data, as the method meant to be a self contain
or I haven't understood a fundamental concept
// Craft stall stock and till program
import java.util.Scanner;
public class revisonTest {
public static void main(String[] args) // where program exicutes
{
final int numOFitems = 50;
String[] item = new String[numOFitems];
int [] broughtItem = new int[numOFitems];
int[] costItem = new int[numOFitems];
int COUNT = getDetail(item,broughtItem,costItem);
System.out.println(item[0]);
}
public static int getDetail(String[] name,int[] quantities,int[]cost)
{
int count =1;
int arrayIndex =0;
String answer = "";
while(!(answer.equals("Exit")))
{
answer = userInput("Item"+count+": ");
if(!(answer.equals("Exit")))
{
name[arrayIndex] = answer;
quantities[arrayIndex] = Integer.parseInt(userInput("How many "+name[arrayIndex]+" have you brought? "));
cost[arrayIndex] = Integer.parseInt(userInput("How much does a "+name[arrayIndex]+" cost? "));
count++;
arrayIndex++;
}
}
return count;
}
public static String userInput(String question)
{
Scanner sc = new Scanner(System.in);
System.out.println(question);
return sc.nextLine();
}
}
String[] item = new String[numOFitems];
This first makes a new treasure map named 'item'.
This makes a new treasurechest capable of containing numOFitems treasuremaps, and buries it in the sand. It is then filled with that many blank maps that lead to no treasure.
This updates your item treasuremap to point at this treasurechest-containing-maps.
getDetail(item,broughtItem,costItem);
This takes your treasuremap to the treasure-of-maps and makes a copy of it, and then hands the copy to the getDetail method. Your copy is unmodified and cannot be modified by getDetail... but that's just your copy of the treasure MAP, not the treasure. Note that getDetail calls this copy name and not item - which it is free to do.
(in getDetail) name[arrayIndex] = answer;
This is getDetail taking its name treasuremap (which is a copy of main's item map), follows the map, gets a shovel out, digs down, finds the treasure, opens it, finds the arrayIndexth map in it, pulls it up, and copies its answer map onto it.
Thus.. when main follows its copy of its map to the same treasure, same thing happens.
Of course, in java we use different jargon.
'treasure' -> 'object'
'treasuremap' -> 'reference'
'follow the map, dig down, open treasure' -> 'dereference'.
'create treasure' -> 'instantiate an object'
There are two different concepts here:
Allocating an array and assigning an array reference to a variable, and
Assigning values to elements in the array
In main, the new operation creates an array of a certain size, and assigns a reference to that array to the variable named item.
The call of getDetail(item,...) makes a copy of that reference (not the array itself) available to the method. Inside getDetail, this reference is stored in what is effectively a local variable, named name.
The loop inside getDetail is collecting answers (which are actually String references) and storing them in successive elements of the array that it knows as name and which the caller knows as item.
name[arrayIndex] = answer;
(Similarly for the other two arrays, of course)
In summary, getDetail is provided with an existing array, into which it writes values.
Incidentally, if the user types too many answers (more than name.length) you'll run off the end of the array, and get an 'index out of bounds' exception.
A String in java is considered a non-primitive data type. So when you created your item array using:
String[] item = new String[numOFitems];
You actually created an empty array of String objects. Based on your code the array has 50 empty spaces where you can store data.
The next part of your code is designed to get input from the user and fill those arrays:
int COUNT = getDetail(item,broughtItem,costItem);
Note: getDetail() never returns the item[] array, so how do you access the data?
When you pass your item array as an argument to the getDetail() method, you are actually passing that array as a reference.
In Java, non-primitive data types are passed as reference. This means that instead of sending the data to the getDetail() method, your actually sending information about where the data is located in memory.
Within your getDetail() method you can manipulate the data and the changes will be reflected on the original array without having to return it.
That is the reason why your print statement shows data in the array:
System.out.println(item[0]);
Any changes made within the getDetail() method, to the array, automatically appear on the original data source.

ArrayList initialized to a size, but index is out of bounds and size is zero. Why?

So I am initializing an ArrayList of ArrayLists in order to have a set of resizable Arrays to hold the names of classes as strings for a Design Analyzer homework assignment. The ArrayLists are initialized to a size of 7 (For the test file that I am using), yet when I perform a get on element 1, I am getting an IndexOutOfBounds exception. Upon checking the size of the ArrayList causing the problem (providers), the size is zero. I am having a hard time understanding why the ArrayList is size zero, despite the fact that I have initialized it to be the size of my cls ArrayList (which is 7 in my test case). The exception is being thrown when I attempt the get(i) on providers in my if statement, but why? Any help would be appreciated.
public class DesignAnalyzer {
//private Hashtable classSet = new Hashtable();
//ArrayList<Integer> counters = new ArrayList<>();
private static ArrayList<ArrayList<String>> providers;
private static ArrayList<ArrayList<String>> clients;
//private static ArrayList<String>[]
public static void analyze(ArrayList<Class<?>> cls, String path){
Package homePkg = Package.getPackage(path.substring(path.lastIndexOf("\\")+1));
ArrayList<ArrayList<String>> providers = new ArrayList<ArrayList<String>>(cls.size());
ArrayList<ArrayList<String>> clients = new ArrayList<ArrayList<String>>(cls.size());
providers.ensureCapacity(cls.size());
clients.ensureCapacity(cls.size());
for(int i = 0; i < cls.size(); ++i){
providers.set(i, new ArrayList<String>());
clients.set(i, new ArrayList<String>());
}
getProviders(cls, homePkg);
getClients(cls);
//providers.clear();
}
private static void getProviders(ArrayList<Class<?>> cls, Package pkg){
for(int i = 0; i < cls.size(); ++i){
Class<?> spr = cls.get(i).getSuperclass();
int temp = providers.size(); //should be 7 in test case, but coming back as zero
if(spr != null && spr.getPackage().toString().equals(pkg.toString()) &&
!providers.get(i).contains(spr.toString())) // exception being thrown here at i = 1 b/c providers.size is zero...
providers.get(i).add(spr.toString());
The number that you pass to ArrayList's constructor is the initial capacity of the ArrayList, not the initial size. The initial size is 0, unless you are passing to the constructor (not the same constructor that takes the capacity parameter) another Collection used to populate your ArrayList.
You should never call providers.get(i) before checking that providers.size() > i.
P.S.
I see that you have code that initializes the providers ArrayList in your analyze method. However, you initialize a local variable and not the static class member of the same name. This means that getProviders should actually throw a NullPointerException and not IndexOutOfBoundsException (unless you initialize the providers static member in some code you didn't include).
Try to change your analyze method to :
public static void analyze(ArrayList<Class<?>> cls, String path){
Package homePkg = Package.getPackage(path.substring(path.lastIndexOf("\\")+1));
providers = new ArrayList<ArrayList<String>>(cls.size());
clients = new ArrayList<ArrayList<String>>(cls.size());
....
Accordingly to the documentation, such a constructor does what follows:
Constructs an empty list with the specified initial capacity.
So, it's perfectly reasonable that accessing it ends in an out of bound exception, for it's empty.
Something similar happens for ensureCapacity, that I see you are using in your code. It simply does what follows:
Increases the capacity of this ArrayList instance, if necessary, to ensure that it can hold at least the number of elements specified by the minimum capacity argument.
I guess you have misunderstood what capacity means for a container like ArrayList and what's actually the size. While the latter actually indicates how many items are there, the former is related to the internal representation of the container. As you know, such a container grows dynamically on demand, but you can slightly optimize it if you know which is the intended final size, as an example when used in algorithms bounded in terms of visited items.

Select subarray without copying into new buffer?

I have float[] array of length 100. Is there a way I can select (pseudocode):
x = array[10:19];
To get elements 10,11,12,...,19 without copying over into another buffer? I'm in a mobile application where I don't want to waste space or time doing this. I'd rather just reference the pointers the system uses for array.
The most efficient way to do this would be to use System.arrayCopy(), which is much faster and more efficient than copying manually using a loop. It will require another array, but any approach you use (beyond just passing the original array around with a couple of ints representing the offset to use) will do this, and it's relatively cheap - the memory consuming bit is usually the objects that it's referencing rather than the array itself, and they are not copied.
No, there is no API to do that. The closest solution to this would be building your own class that wraps an existing array, and does the re-indexing:
class SubArray {
private final float[] data;
private final int offset;
private final int length;
public SubArray(float[] data, int offset, int length) {
this.data = data;
this.offset = offset;
this.length = length;
}
public float get(int index) {
if (index >= length) throw ...
return data[index + offset];
}
public void set(int index, float value) {
if (index >= length) throw ...
data[index + offset] = value;
}
}
If the result that you need is a new object that behaves like an array in all respects, including the indexing operator, you would need to make a copy.
(Update) Precondition: You should store the data in a Float[] instead of a float[], the performance-hit should be minimal.
You can use: Arrays.asList(array).subList(10, 20).
The Arrays.asList(array) does the following:
Returns a fixed-size list backed by the specified array. (Changes to the returned list "write through" to the array.) This method acts as bridge between array-based and collection-based APIs, in combination with Collection.toArray(). The returned list is serializable and implements RandomAccess.
Source
And then .subList(10, 20) returns you a List.
Then if you really want to work with arrays in the end, you could take the following lines:
List<Float> subList = Arrays.asList((Float[])array).subList(10, 20);
Float[] subArray = subList.toArray(new Float[subList.size()]);
(Update) Changed Arrays.asList(array) to Arrays.asList((Float[])array) such that it is correct now.
From documentation:
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.
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.)
Like the toArray() method, this method acts as bridge between array-based and collection-based APIs. Further, this method allows precise control over the runtime type of the output array, and may, under certain circumstances, be used to save allocation costs.
Suppose x is a list known to contain only strings. The following code can be used to dump the list into a newly allocated array of String:
Source
This should ensure that no data is wasted, the only thing to be careful about could be autoboxing.
UPDATE: Changed my answer such that it now is correct under a precondition.
What is the problem of using a simple for loop? Objects are in java called by reference.
So, executing copying the array does not copy the objects.
float[] subarray = new float[10];
for(int i = 10, j = 0; i < 19; i++, j++) {
subarray[j] = x[i];
}
The array[0] is a reference to the object of x[0].
edit: This only applies for objects, and i don't know if it also applies to a float

how can i initialize my array when i cant initialize as null?

i have an array of strings which i want to convert to int, pretty simple and straightforward here is the code :
public static void main(String[] args) {
String myarray[]=readfile("[pathtothefile]");
int mynums[] = new int[myarray.length];
for (int i=0;i<myarray.length;i++){
mynums[i]=Integer.parseInt(myarray[i]);
}
System.out.print(Arrays.toString(mynums));
}
But the Problem here is, if i initialize "mynums" like this: mynums[]=null; i get NullPointerException on the following line:
"mynums[i]=Integer.parseInt(myarray[i]);"
what i have to do to solve it is
int mynums[] = new int[myarray.length];
here someone explained why it happens but i dont know how to initialize now! i mean sometimes i dont know how big my array can get and i just want to initialize it. is it even possible?
In Java everything is a pointer behind the scenes. So when you do mynums[]=null, you are pointing to a null. So what is null[i]? That is where your NPE comes from. Alternatively when you point it to an array, then you are actually accessing the i'th element of the array.
You have to first initialize the array because it allocates memory depending on the array size. When you want to add for example an integer to an array it writes the int into previously allocated memory.
The memory size won't grow bigger as you add more items.( Unless you use Lists or Hashmaps, ... but it's not true for generic arrays)
If you don't know how big your array will be, consider using SparseIntArray. which is like Lists and will grow bigger as you add items.
Briefly, in java an array is an object, thus you need to treat it like an object and initialize it prior to doing anything with it.
Here's an idea. When you're initializing something as null, you're simply declaring that it exists. For example ... if I told you that there is a dog, but I told you nothing about it ... I didn't tell you where it was, how tall it was, how old, male/female, etc ... I told you none of its properties or how to access it, and all I told you was that there IS a dog (whose name is Array, for sake of argument), then that would be all you know. There's a dog whose name is Array and that is it.
Typically, arrays are used when the size is already known and generally the data is meant to be immutable. For data that are meant to be changed, you should use things like ArrayList. These are intended to be changed at will; you can add/remove elements at a whim. For more information about ArrayList, read up on the links posted above.
Now, as for your code:
public static void main(String[] args) {
ArrayList<int> myInts = new ArrayList<int>();
// define a new null arraylist of integers.
// I'm going to assume that readfile() is a way for you get the file
// into myarray. I'm not quite sure why you would need the [], but I'll
// leave it.
String myarray[] = readfile("[pathtothefile]");
for (int i = 0; i < myarray.length; i++) {
//adds the value you've specifed as an integer to the arraylist.
myInts.add(Integer.parseInt(myarray[i]));
}
for (int i = 0; i < myInts.size(); i++) {
//print the integers
System.out.print(Integer.toString(myInts.get(i)));
}
}
What if you don't use an array but an ArrayList? It grows dynamically as you add elements.

Make a copy of a variable and add to Arraylist

I'm working with two-dimensional array-values that should be inserted into a ArrayList. But this is done in a for-loop and the value of the two-dimensional array-value gets changed as the loop runs since it is just used as an temp-variable (which makes all of the variables stored in the ArrayList gets changed as this variable changes).
So if I try to print out the content of the ArrayList when the loop is done all the values are the same.
for(int i = 0; i <= Counter; i++)
{
if(Xhavetomove >= i)
arrayvalue[0][0] = this.Xspeed;
else
arrayvalue[0][0] = 0;
if(Yhavetomove >= i)
arrayvalue[0][1] = this.Xspeed;
else
arrayvalue[0][1] = 1;
System.out.println(arrayvalue[0][1]);
Object.movement.add(arrayvalue);
}
Are there anyway I can make it store the value itself?
For example: The first time the loop runs the value is "5,5" but if I print out the ArrayList when the loop is done all the values has turned into "5,1".
The problem is the way Array is added to the Object here. You are not adding the Array to the Object. What is happening is you are adding the address to the location in memory where the Array resides. So every time you add the Array to the Object, you are adding the same address every time. So every Array in the Object is actually the same Array over and over since they all point to a single location in memory. So when you change the Array, it will appear to change all of them inside the Object.
The best thing to do is either create a new Array every time through the loop, essentially creating a new location in memory for the Array to reside, or clone() the Array which will create a new reference.
Example:
String[] houseOfStark = {"Eddard", "Catelyn",
"Robb", "Sansa", "Arya", "Bran", "Rickon"}; // Sorry Jon
String[] copyOfStark = houseOfStark;
String[] cloneOfStark = houseOfStark.clone();
houseOfStark[1] = "Lady Catelyn";
System.out.println(houseOfStark[1]);
System.out.println(copyOfStark[1]);
System.out.println(cloneOfStark[1]);
Will produce:
Lady Catelyn
Lady Catelyn
Catelyn
Good blog post explaining the difference
At the end each add needs to create an own object.
To use clone is one way.
Or to add always the values in pairs, in an other.
A totally different way is to use serialization. This make sense when you do not want to calculate this values but to log it. In this case you need an outputStream
What is best is defined by what you want to do with Object.movement
You need to use array's clone() method to make its copy:
//for example
int[][] copy = (int[][])arraySource.clone();

Categories