Why do i get different behaviors with:
Collection col2 = new ArrayList(col);
Collection col2 = new ArrayList();
col2.addAll(col)
I'm working with viewers, and the code is complex, and i'm trying to explain the "root" of the problem. Another interesting fact is the next one...
//IF i use this code i have the correct behavior in my app:
public void updateCollection(Collection<Object> col) {
this.objectCollection.clear();
this.objectCollection.addAll(col);
}
//IF i use this code i have unexpected behavior in my app:
public void updateCollection(Collection<Object> col) {
this.objectCollection=new ArrayList(col);
}
This code works:
public void updateCollection(Collection<Object> col) {
this.objectCollection.clear();
this.objectCollection.addAll(col);
}
But this introduces problems:
public void updateCollection(Collection<Object> col) {
this.objectCollection = new ArrayList(col);
}
I suspect that this variation on your first method would introduce identical problems:
public void updateCollection(Collection<Object> col) {
this.objectCollection = new ArrayList();
this.objectCollection.clear();
this.objectCollection.addAll(col);
}
Why? Evidently you have another reference to objectCollection in use somewhere. Somewhere in your code, another object is saying (for instance):
myCopyOfObjectCollection = theOtherObject.objectCollection;
If you're using a getter, that doesn't change the underlying behavior - you are still keeping another reference around.
So if on initial assignment, say, the collection contained {1, 2, 3}, you start out with:
this.objectCollection: {1, 2, 3}
that.copyOfObjectCollection: {1, 2, 3}
When you assign a new ArrayList to this.objectCollection, and populate it with, say, {4, 5, 6}, you get this:
this.objectCollection: {4, 5, 6}
that.copyOfObjectCollection: {1, 2, 3}
So that is still pointing to the original ArrayList.
Collection col2 = new ArrayList(col);
will create a new ArrayList with size col.size() (+10%) and copy all elements from col into that array.
Collection col2 = new ArrayList();
will create a new ArrayList with initial size of 10 (at least in Sun implementation).
col2.addAll(col);
will copy all elements from col into the end of the col2 ArrayList, enlarging the backing array size, if needed.
So, depending on your col collection size, the behavior will be a bit different, but not too much.
It is preferable to use the first option - that will avoid at least one extra backing array expansion operation.
public List getAdminImIdsWithValidShortNames(){
return adminImIdsWithValidShortNames;
}
public void setAdminImIdsWithValidShortNames(List adminImIdsWithValidShortNames){
this.adminImIdsWithValidShortNames=adminImIdsWithValidShortNames;
}
I think, easy is beautiful, just generator setter/getter method is a good habit.
if you first clear, then addAll, the list need to clear all elements of list, then addAll will be extra backing array expansion operation, that's not science.
just replacement, this variable will be point to new List, the old list will be auto-GC.
Related
I am working on a Java project and have a number of arrays generated by a different script (e.g. {1, 2, 3, 5}).
I have these arrays in a notepad and would like to pass them directly to a function which would put them in a hasmap of the form HashMap<String, Int[]>. These Hashmaps will subsequently be placed in another hashmap.
Now I wrote a function that took in the parameters necessary to create the larger hash (thus Hashmap<Int, Hashmap<String, Int[]>>) which takes Int, String, and Int[] as function parameters.
My problem is that Java wants me to first make up a Int[] (e.g. array_of_numbers1 = {1, 2, 3}) before allowing me to pass that Int[] as a parameter in the function that creates the HashMap.
Is there any way for me to do this directly, wihtout the need to pass to first create a Int[] variable before passing that into the function? So directly pass an array into a function call?
I would love for my function call to look like (where my function is: Public Void AddElement(Int, String, Int[]))
AddElement(1, "Numbers1", {1,2,3}) etc.
Is this way of passing parameters even possible? Could it be done in a different way so that I could still use a list, or a sequence of numbers which would together be placed in an array (in the function) so that I could would not have to initialize a Int[] array first (such as AddElement(1, "Numbers1", 1, 2, 3,....possibly more numbers or not))?
The error I get is "Array initializer not allowed here". What could be done to work around this problem?
Thanks in advance!
The syntax is:
AddElement(1, "Numbers1", new int[]{1,2,3})
——
Only when declaring a variable/field can you use the shorthand version, eg
int[] myArray = {1, 2, 3};
The standard form of making an array, initialized with numbers of your choosing, is:
new int[] {1,2,3}, e.g:
addElement(5, "Hello", new int[] {1, 2, 3});
You can omit the new int[] part of that, but only if you use this expression as the initial value of a new field or variable declaration, and not when passing to a method call:
int[] example = {1, 2, 3};.
If you make your method argument 'varargs', you can just pass an infinite amount of int parameters, though. You are probably looking for this:
public void addElement(int rowKey, String columnKey, int... values) {
map
.computeIfAbsent(rowKey, r -> new HashMap<>())
.computeIfAbsent(columnKey, c -> new HashMap<>())
.put(values);
}
addElement(5, "Hello", 1, 2, 3);
You may get a warning here in some IDEs, which is trying to say that if someone creates an array, and uses that to call a varargs method, they can then later change the array and this changes the array in your map store:
int[] example = {1, 2, 3};
addElement(5, "Hello", example);
example[0] = 6;
System.out.println(getElement(5, "Hello")[0]);
// prints 6 - that may not be what you want
If you dislike this; make a copy of the array in your addElement method: .put(Arrays.copyOf(values));
Note that this problem applies if you use int[] too; it's just assumed that you know about it if you write int[], so most IDEs don't generate the warning then.
I would love for my function call to look like (where my function is: Public Void AddElement(Int, String, Int[]))
AddElement(1, "Numbers1", {1,2,3}) etc.
Use varargs.
public void addElement(int i, String s, int... values)
addElement(1, "Numbers1", 1, 2, 3);
There can only be one varargs parameter, and it must be the last parameter.
Also, notice how almost all the words in the code starts with lowercase letter. The code in the question is not Java.
Why this question is not a possible duplication of How Arrays.asList(int[]) can return List<int[]>?.
That question doesn't really answer my particular situation as I am trying to figure out if there is a discrepancy in my use of Arrays.copyOf.
CASE 1: Supposed deep copy of the array
// Creating a integer array, populating its values
int[] src = new int[2];
src[0] = 2;
src[1] = 3;
// Create a copy of the array
int [] dst= Arrays.copyOf(src,src.length);
Assert.assertArrayEquals(src, dst);
// Now change one element in the original
dst[0] = 4;
// Following line throws an exception, (which is expected) if the copy is a deep one
Assert.assertArrayEquals(src, dst);
CASE 2:
Here is where things seem to be weird:
What I am trying to do with the below method (lifted verbatim from a book) is to create an immutable list view of a copy of the input array arguments. That way, if the input array changes, the contents of the returned list don't change.
#SafeVarargs
public static <T> List<T> list(T... t) {
return Collections.unmodifiableList(new ArrayList<>(Arrays.asList(Arrays.copyOf(t, t.length))));
}
int[] arr2 = new int[2];
arr2[0] = 2;
arr2[1] = 3;
// Create an unmodifiable list
List<int[]> list2 = list(arr2);
list2.stream().forEach(s -> System.out.println(Arrays.toString(s)));
// Prints [2, 3] as expected
arr2[0] = 3;
list2.stream().forEach(s -> System.out.println(Arrays.toString(s)));
// Prints [3, 3] which doesn't make sense to me... I would have thought it would print [2, 3] and not be affected by my changing the value of the element.
The contradiction that I see is that in one case (Case 1), Arrays.copyOf seems to be a deep copy, whereas in the other case (Case 2), it seems like a shallow one. The changes to the original array seem to have written through to the list, even though I have copied the array in creating my unmodifiable list.
Would someone be able to help me resolve this discrepancy?
First of all, your list method performs an unnecessary step, you don't need the copyOf operation, so here goes:
#SafeVarargs
public static <T> List<T> list(T... t) {
return Collections.unmodifiableList(
new ArrayList<>(Arrays.asList(t))
);
}
The ArrayList constructor already copies the incoming list, so you're safe there.
Next, when you are calling your list() method with an int[], that array is considered to be a single element of type int[], because the type erasure of your T... is Object..., and int is primitive. There is no way you can make your method do a deep copy inside the list without either changing the parameter types or doing an instanceOf check and performing the copy manually inside the method. I'd say the wisest thing to do is probably to move the Arrays.copyOf() call outside the method:
List<int[]> list2 = list(Arrays.copyOf(arr2));
I am trying to create an array of arrays of arrays etc..., except I don't know how many nested levels deep it needs to be until runtime.
Depending on the input, I might need either int[], int[][], int[][][][][][], or anything else. (For context, I am trying to construct an N-dimensional grid for a cellular automaton, where N is passed as a parameter.)
I don't have any code for you because I have no idea how to go about this; I suspect is not possible at all using just arrays. Any help, or alternative solutions, would be appreciated.
You could do this with an Object[], limiting its members to either Object[] or int[].
For example, here's an array that goes three levels deep in one part, and two levels deep in another:
Object[] myarray = new Object[] {
new Object[] { new int[] { 1, 2 },
new int[] { 3, 4 }},
new int[] { 5, 6 }
};
After you've created it, you may want to access members. In your case, you know the depth N up front, so you know at what depth to expect an Object[] and at what depth to expect an int[].
However, if you didn't know the depth, you could use reflection to determine whether a member is another Object[] level or a leaf int[].
if ( myarray[0] instanceof Object[] ) {
System.out.println("This should print true.");
}
EDIT:
Here's a sketch [untested so far, sorry] of a method that access a member of an array of known depth, given an array of indices. The m_root member can be an Object[] or an int[]. (You could relax this further to support scalars.)
public class Grid {
private int m_depth;
private Object m_root;
...
public int get( int ... indices ) {
assert( indices.length == m_depth );
Object level = m_root;
for ( int i = 0; i + 1 < m_depth; ++i ) {
level = ((Object[]) level)[ indices[i] ];
}
int[] row = (int[]) level;
return row[ indices[m_depth - 1] ];
}
}
This should be achievable using Object[], since arrays are objects:
int[] arr = {1,2,3};
int[] arr2 = {1,2,3};
int[] arr3 = {1,2,3};
int[] arr4 = {1,2,3};
Object[] arr5 = {arr, arr2}; // basically an int[][]
Object[] arr6 = {arr3, arr4}; // basically an int[][]
Object[] arr7 = {arr5, arr6}; // basically an int[][][]
// etc.
Note that one array doesn't have to contain arrays of the same dimensions:
Object[] arr7 = {arr5, arr};
To prevent this (and to allow for easier access to the data), I suggest writing a class which has an Object member (which will be your int[] or Object[]) and a depth variable and some nice functions to give you access to what you want.
ArrayLists will also work:
ArrayList array = new ArrayList();
array.add(new ArrayList());
array.add(new ArrayList());
((ArrayList)array.get(0)).add(new ArrayList());
// etc.
As your N increases going with nested arrays becomes less and less advantageous, especially when you have a grid structure. Memory usage goes up exponentially in N with this approach and the code becomes complex.
If your grid is sparsely populated (a lot of cells with the same value) you can instead have a collection of Cell objects where each of these holds a coordinate vector and the integer value of the cell. Every cell that is not in the collection is assumed to have a default value, which is your most common value.
For faster access you can use for example a k-d tree (https://en.wikipedia.org/wiki/K-d_tree) but that depends a bit on your actual use-case.
#Andy Thomas explains how to do this using Object[] for the higher levels of the multidimensional array. Unfortunately, this means that the types are not correct to allow indexing, or indeed to allow element access without typecasts.
You can't do this:
Object[] array = ...
int i = array[1][2][3][4];
To get types that allow you to do the above, you need to create an object whose real type is (for example) int[][][][].
But the flipside is that it is not really practical to use that style of indexing for N dimensional arrays where N is a variable. You can't write Java source code to do that unless you place a bound on N (i.e. up to 5) and treat the different cases individually. That becomes unmanageable very quickly.
You can use Java reflection as Arrays are objects.
public static void main(String[] args) throws InstantiationException,
IllegalAccessException, ClassNotFoundException {
Class<?> intClass = int.class;
Class<?> oneDimensionalArrayClass = Class.forName("[I");
Object oneDimensionalIntArray1 = Array.newInstance(intClass, 1);
Array.set(oneDimensionalIntArray1, 0, 1);
Object oneDimensionalIntArray2 = Array.newInstance(intClass, 1);
Array.set(oneDimensionalIntArray2, 0, 2);
Object oneDimensionalIntArray3 = Array.newInstance(intClass, 1);
Array.set(oneDimensionalIntArray3, 0, 3);
Object twoDimensionalIntArray = Array.newInstance(oneDimensionalArrayClass, 3);
Array.set(twoDimensionalIntArray, 0, oneDimensionalIntArray1);
Array.set(twoDimensionalIntArray, 1, oneDimensionalIntArray2);
Array.set(twoDimensionalIntArray, 2, oneDimensionalIntArray1);
System.out.println(Array.get(Array.get(twoDimensionalIntArray, 1), 0));
}
The class Array with its static methods gives access on items while you can specify the dimension of your arrays with the number of leading "[".
The whole construct of multi-dimensional arrays is just the compiler doing some work for you on a big block of memory (ok as some have commented in java this is multiple blocks of memory). One way to deal with the problem you face is to use nested arraylists at runtime. Another (more performant) way is to just allocate a single-dimensional array of the size you need and do the indexing yourself. You could then hide the indexing code in a method that was passed all the details like an array de-reference.
private int[] doAllocate(int[] dimensions)
{
int totalElements = dimensions[0];
for (int i=1; i< dimensions.length; i++)
{
totalElements *= dimensions[i];
}
int bigOne = new int[totalElements];
return bigOne;
}
private int deReference(int[] dimensions, int[] indicies, int[] bigOne)
{
int index = 0;
// Not sure if this is only valid when the dimensions are all the same.
for (int i=0; i<dimensions.length; i++)
{
index += Math.pow(dimensions[i],i) * indicies[dimensions.length - (i + 1)];
}
return bigOne[index];
}
Fields like you wrote above a checked and created by the compiler. If you want a dynamic data structure during runtime you could create your own data structure. Search for Composite Pattern. A small snippet should show you how it works:
interface IGrid {
void insert(IGrid subgrid);
void insert(int[] values);
}
class Grid implements IGrid {
private IGrid subgrid;
void insert(IGrid subgrid) {this.subgrid = subgrid;}
void insert(int[] values) {/* Do nothing */}
}
class SubGrid implements IGrid {
private int[] values;
void insert(IGrid subgrid) {/* Do nothing */}
void insert(int[] values) {this.values = values;}
}
You could simply create a Subgrid for int[] or a Grid with a Subgrid for int[][]. It's only a rudimental solution, you would have to create some code for working on your automaton's levels and values. I would do it this way. Hope it will help :) And look forward for more solutions^^
I was trying to understand the execution logic behind the following block of code:
Integer[][] mdArray = new Integer[][] { { 1, 2 }, { 3, 4 }, { 5, 6 },
{ 7, 8 } };
List<Integer[]> mdArrayList = new ArrayList<Integer[]>();
// Iterate through every row of mdArray
for (Integer[] obj : mdArray) {
mdArrayList.add(obj);
}
/* Throws ClassCastException because it cannot resolve the final array size?
*
* i.e. it is a proper RTError.
*
*/
Integer[][] toArray = (Integer[][]) (mdArrayList.toArray());
The above code compiles fine but throws a ClassCastException at runtime when executing the final line. My understanding is that JVM cannot execute this code in runtime because even though toArray() chucks out an array of elements in mdArrayList, the destination type cannot be resolved or promoted to a valid one?
Any explanation will be appreciated! I am simply trying to understand if this sort of code execution must be avoided or exception-handled to outut something more appropriate to user needs.
You shouldn't be casting. Pass an Integer[][] to toArray. Something like
Integer[][] toArray = mdArrayList.toArray(new Integer[][] {});
System.out.println(Arrays.deepToString(toArray));
Output is
[[1, 2], [3, 4], [5, 6], [7, 8]]
Try
Integer[][] toArray = mdArrayList.toArray(new Integer[mdArrayList.size()][]);
If you don't provide an argument to the toArray method, the return type will always be Object[] regardless of the actual type of what is inside the list.
If you do provide an argument, you can either choose to make it a zero-length array (new Integer[0][]), in which case the toArray method will create a new array of the same type; or you can provide an array if the correct size, and then the toArray method will use that array. The latter is slightly more efficient than the former, but not something that you need to worry much about.
Take the following example:
private int[] list;
public Listing() {
// Why can't I do this?
list = {4, 5, 6, 7, 8};
// I have to do this:
int[] contents = {4, 5, 6, 7, 8};
list = contents;
}
Why can't I use shorthand initialization? The only way I can think of getting around this is making another array and setting list to that array.
When you define the array on the definition line, it assumes it know what the type will be so the new int[] is redundant. However when you use assignment it doesn't assume it know the type of the array so you have specify it.
Certainly other languages don't have a problem with this, but in Java the difference is whether you are defining and initialising the fields/variable on the same line.
Try list = new int[]{4, 5, 6, 7, 8};.
Besides using new Object[]{blah, blah....} Here is a slightly shorter approach to do what you want. Use the method below.
public static Object [] args(Object... vararg) {
Object[] array = new Object[vararg.length];
for (int i = 0; i < vararg.length; i++) {
array[i] = vararg[i];
}
return array;
}
PS - Java is good, but it sucks in situations like these. Try ruby or python for your project if possible & justifiable. (Look java 8 still has no shorthand for populating a hashmap, and it took them so long to make a small change to improve developer productivity)