Why is this Java array considered two dimensional? - java

Consider this code:
class arraytest {
public static void main(String args[]) {
int[] a = null , b[] = null;
b = a;
System.out.println( b );
}
}
The line
b = a;
is flagged by the compiler saying:
Incompatible types, found int[], required int [][]
Why is b considered two dimensional? I realize the "shortcut" declaration
int[] a = null , b[] = null;
is to blame, but why does it make the array two dimensional when only one set of brackets have been written? I find this syntax unclear and obfuscating.

Take this example:
int a, b;
Then both a and b are ints, right? So now take this:
int[] a, b;
The both a and b are int arrays. So by adding another set of brackets:
int[] a, b[];
you have added another set of brackets to b.
Either of these would be fine:
int[] a = null , b = null;
int a[] = null , b[] = null;
or as you say, simply putting them on separate lines would work too (and be much easier to read).

int[] a = null , b[] =null;
.... it's equal to :
int[] a = null;
int[][]b = null;
You have to do :
int[] a = null , b =null;

In addition to the other answers, Java does not have multi-dimensional arrays. Java has arrays of any type. This "any type" may itself be an array. Multi-dimensional arrays are distinctly different, although subtly.

If you take out the a declaration, this is what you have:
int[] b[] = null;
which is, unfortunately, a legal alternative to
int[][] b = null;

I was lead to believe that the syntax of allowing the [] after the b was there solely for the C-language crowd who would feel more at home with it. Unfortunately, it has the side effect of allowing confusing declarations like
int[] b[];
which ends up the same as
int[][] b;

Related

How does pointing to objects work in java?

So, lets say I have 2 arrays defined like this:
int[] a = {1,2,3};
int[] b = a;
a[1] = 35; // b[1] becomes 35
b[0] = -5; // why would a[0] become -5?
So, I can reason that changing a value in a will automatically change the value in b (b is pointing to a). But, if I change value in b, why would it affect a? Wouldn't that be as if a is pointing to b (they are interchangeable)?
I was confused with this concept and would like some clarification.
Any advice would be appreciated. Thanks.
Let's do an analogy here. The objects (such as arrays) that you create are like balloons. And the variables (a and b) are like children. And the children can hold a single balloon with a string. And that string is what we call a reference. On the other hand, one balloon can be held by multiple children.
In your code, you created a single balloon i.e. an array {1, 2, 3}. And let a child called a hold it. Now you can tell child a to modify items in the array because the child a is holding it.
In the second line, you tell another child b to hold the balloon that a is holding, like this:
int[] b = a;
Now a and b are actually holding the same balloon!
When you do this:
a[1] = 35;
Not only is the balloon that a is holding been changed, but also is the balloon held by b been changed. Because, well, a and b are holding the same balloon.
The same thing happens when you do:
b[0] = -5;
That's how reference types work in Java. But please note that this doesn't apply to primitive types, such as int or float.
For example,
int a = 10;
int b = a;
b = 20; //a is not equal to 20!!!
When you create the variable int[] a = {1,2,3};, by using the brackets { } just as by saying new int[n] java allocates enough memory for those values and now a references that address in memory.
Then, when you create the new variable int[] b = a;, you aren't allocating memory to that variable b, rather b references the same address which a references.
Now, when you change something in a or b the change occurs at the address referenced by both those variables and will be reflected by both.
Because Java Array is considered Reference Object.
int[] b = a; // variable 'b' points same memory location with 'a'
If you want to copy all value of a, write code below
#Test
public void arrayCopy1() {
int[] a = {1, 2, 3};
int[] b = a;
System.out.println(a); //Point [I#64a294a6
System.out.println(b); //Point [I#64a294a6
printArrayValues(a); //[1,2,3]
printArrayValues(b); //[1,2,3]
a[0] = 10;
printArrayValues(a); //[10,2,3]
printArrayValues(b); //[10,2,3]
}
#Test
public void arrayCopy2() {
int[] a = {1, 2, 3};
int[] b = Arrays.copyOf(a, a.length);
System.out.println(a); //Point [I#64a294a6
System.out.println(b); //Point [I#7e0b37bc
printArrayValues(a); //[1,2,3]
printArrayValues(b); //[1,2,3]
a[0] = 10;
printArrayValues(a); //[10,2,3]
printArrayValues(b); //[1,2,3]
}
private void printArrayValues(int[] arr) {
System.out.print("[");
for(int i = 0 ; i < arr.length ; i++) {
System.out.print(arr[i]);
if (i != arr.length-1)
System.out.print(",");
}
System.out.println("]");
}
Because when you set int[] b = a you didn't simply copied the values of a and gave them to b you gave to b the memory location of a, therefore if you change one, you change the other

Java copying understanding

After testing the code (see below), I found out that I don't understand some fundamentals.
Class A.
class A {
private String s;
private int[] array;
private B b;
public A(String s, int[] array, B b) {
this.s = s;
this.array = array;
this.b = b;
}
}
Class B.
class B {
public int t;
public B(int t) {
this.t = t;
}
}
I thought that any changes I did after A a = new A(s, array, b); would affect a. Don't all the fields of a and the variables s, array, b refer to the same object?
String s = "Lorem";
int array[] = new int[] {
10, 20, 30
};
B b = new B(12);
A a = new A(s, array, b);
s = "Dolor";
array = new int[] {
23
};
b = new B(777); // Initialized with a new value, a.b keeps the old one. Why?
System.out.println(a);
The output.
String Lorem
Array [10, 20, 30]
B 12
And about this.
B b2 = new B(89);
B b3 = b2;
System.out.println(b3);
b2 = null;
System.out.println(b3); // b2 initialized with null, b3 keeps the old one. Why?
The output.
89
89
However, if I have two lists, this shows that they both refer to same object.
ArrayList<String> first = new ArrayList<String>();
first.add("Ipsum");
ArrayList<String> second = new ArrayList<String>();
second = first;
first.add("The Earth");
System.out.println(second);
The output.
[Ipsum, The Earth]
The difference is assignment versus modification.
Assignment (=) makes the variable point to something else, so this won't change the underlying data. So any other variables pointing to the same data don't change.
Modification (pretty much anything except =) doesn't change what the variable points to, it just modifies the underlying object. So any other variables pointing to the same data do change.
For you example:
b = new B(777); is assignment, so only b is changed to point to something else. a.b won't change.
b2 = null; is assignment, so only b2 is changed to point to something else. b3 won't change.
If you were to say b2.t = 5, this would be modification (we're not assigning a new value to b2, we're modifying it by changing one of its members), so b3 will change as well.
I hope that explains it.
No. The thing is, you are not changing a, you are assigning a new value to s. S is a String, which are immutable, which means you can never make a change to the value of s. You can, however, change the reference in S, which is what you are doing.
To make yourself more clear try these lines of code..
String s = "Lorem";
int array[] = new int[] {10, 20, 30};
B b = new B(12);
//A a = new A(s, array, b);
s = "Dolor";
array = new int[] {23};
b = new B(777);
A a = new A(s, array, b);
System.out.println(a);
ArrayList<String> first = new ArrayList<String>();
first.add("Ipsum");
ArrayList<String> second = new ArrayList<String>();
second = first;
second.add("The Earth");
first.remove("The Earth");
System.out.println("second :"+second);
What are the current values the String s, array and the object b are holding while creating the instance of class A ( at the time of calling class A constructor ) will be printed. After creating class A instance, the String s, array and object b will be referred as a.s, a.array and so. If you assign a new value to s, array and b, it wont affect the class A instance.
And for the array list question, the two array lists will refer the same reference only. If you want different reference then do like this... (But always = assign operator will make same reference only )
ArrayList<String> first = new ArrayList<String>();
first.add("Ipsum");
ArrayList<String> second = new ArrayList<String>(first);
second.add("The Earth");
System.out.println("first :"+first);
System.out.println("second :"+second);
Thanks Dukeling for explaing how assignments works with objects and primitives, I am adding here to explain how the list works when operations performed on them.
When we consider the two array lists created in the above code in the question, both the variables first and second are pointing to the same array object that resides in the memory.
So when an add operation is performed the underlying object itself gets updated. So the print operation prints the second array list which pointed to the same array-list object created during the creation of first array-list.

How to get rid of pointers?

To make my question understandable, I'm using the following example code. The output of this code is 5, while I wanted it to be 3. I'm guessing that B is working as a pointer for A, but I want A to be copied in B initially, and subsequent changes in A should not affect B.
import java.io.*;
public class fg
{
public static void main(String args[]) throws Exception
{
int[] A = new int[3];
A[0]=1;
A[1]=3;
A[3]=7;
check(A);
}
public static void check(int[] A)
{
int[] B = A;
A[1] = 5;
System.out.println(B[1]);
}
}
You need to explicitly create a copy,
int[] B = Arrays.copyOf(A, A.length);
since int[] is a reference type, so an assignment just copies the reference (basically, pointer).
If the array elements themselves are not primitive types, you probably will need a deep-copy, so
type B[] = new type[A.length];
for(int i = 0; i < A.length; ++i) {
B[i] = makeCopyOf(A[i]);
}
where makeCopyOf() should create a sensible copy of a type instance. If you don't find anything better, type makeCopyOf(type orig) { return orig.clone(); } could serve as a fallback.
B is a reference, and as such is analogous to a pointer (with various differences - you can't do arithmetic and it's not a direct memory pointer).
As such, you'll have to create a copy of the structure (the array) that you're referring to. Note that if you're copying objects, you may need a deep copy. You would likely have to do this in a copy constructor (see this article as to why clone() is considered broken)

Java: Generic Static Multidimensional Arrays

If it is possible, how can I create a static multidimensional array in Java with different primitive datatypes per dimension?
By static, I mean the primitive array that is not dynamic like an ArrayList would be.
You can't.
A multidimensional array is, by definition, an array of arrays of arrays .... of something. So there's no way for any of those dimensions except the last to be anything other than an array. At least, by the traditional definition anyway. But if you mean something else by "multidimensional array", you'll need to tell us what that is.
As for "static", that is a heavily overloaded word in programming and every language that I can think of uses it to mean something slightly different. In Java, static means "belongs to a class, rather than to instances of that class." Again, if you mean something else by "static" here, you'll need to tell us what that is.
Edit: As originally posted, the question didn't include the word "primitive". That changes it a bit. Indeed, I agree that it would be nice from a convenience standpoint if Java allowed arrays to be indexed by char or even an enum rather than just int. But it doesn't.
Dimensions in an Array are always from type int. Think about it!
int a = 4;
int b = 5;
Shoe shoe = new Shoe (Color.RED, 42, "Leather");
Hat hat = new Hat (17, Color.Black);
Foo foo = foos[a][b];
Zilch pop = bars[shoe][hat]; // no go
If you have a multidimensional array of Foos, the first dimension is an Foo, the second an array of Foos, the third an array of array of Foo. The only variable type is the one at the bottom.
Edit after update of question:
Arrays aren't called static or primitive. Their size is fixed on initialization, and what they have in common with primitives is, that they are a buildin, which is threated special in some cases. They are - in contrast to the so called primitive types, which aren't that primitive (they have, for example, operators, exclusively for their own, like * / -) but meanwhile they are objects, but not declared in the library.
Call them build in-types.
Using Bhesh Gurung's trick:
Object[] arr = {new Integer[]{}, new String[]{}, new Double[]{}};
is begging for trouble, and it is not made of different datatypes per dimension. Let's start with the dimensions:
// One-dimensional object:
JPanel [] panels = new JPanel [3];
// Two-dimensional object:
JPanel [][] panels = new JPanel [3][10];
You have JPanels on the bottom level, and an Array of JPanel on the next dimension. You can add more dimension, and will always get an additional (Array of ...) wrapped around.
You can not mix different datatypes in an Array like int and char, or JPanel and JFrame, or int and JButton. Only if you abstract over the difference, and use an JComponent for JPanel and JFrame as common parent, but this will not work for the build-in types int, char, boolean and so on, because they aren't objects.
But can't you use autoboxing, and use Integer instead of int, Character instead of char, and then use Object as common parent class? Yes, you could, but then you're not using the primitives any more, and you're begging for troubles.
Dan is talking about a different thing - using differnt types for indexing in the multi-dimensional array:
byte b = 120;
short s = 1000;
String o [][] = new String[b][s];
b = 7;
s = 9;
o[b][s] = "foobar";
String foo = o[b][s];
There is no problem using bytes or shorts, but you can't restrict the size of an Array by declaring it as byte or short. In most cases the boundaries of a build-in integer type will not fit to a datatype (think 365 days per year), especially, since all types might get negative, so bounds-checking is necessary although and can't be restricted to compile time.
But now to the trouble:
We could declare the array as two-dimensional from the beginning:
Object[][] ar2 = {
new Integer [] {4, 5, 6},
new String [] {"me", "and", "you"},
new Character [] {'x', 'y', 'z'}};
That works fine, and makes the inner arrays accessible immediately without casting. But it is only known for the compiler, that the elements are Object arrays - the underlying type is abstracted away, and so we can write:
ar2[1][1] = 17; // expected: String
ar2[2][0] = "double you"; // expected: Char
This will compile flawlessly, but you're shooting yourself in the foot and get a Runtime exception for free.
Here is the source as a whole:
public class ArrOfMixedArr
{
public static void main (String args[])
{
Object[] arr = {
new Integer [] {1, 2, 3},
new String [] {"you", "and", "me"},
new Character [] {'a', 'b', 'c'}};
show (arr);
byte b = 7;
short s = 9;
String o [][] = new String[200][1000];
o[b][s] = "foobar";
String foo = o[b][s];
Object[][] ar2 = {
new Integer [] {4, 5, 6},
new String [] {"me", "and", "you"},
new Character [] {'x', 'y', 'z'}};
show (ar2);
// exeptions:
ar2[1][1] = 17; // expected: String
ar2[2][0] = "double you"; // expected: Char
}
public static void show (Object[] arr)
{
for (Object o : arr)
{
if (o instanceof Object[])
show ((Object[]) o);
else
System.out.print (o.toString () + "\t");
}
System.out.println ();
}
}
Now what is the solution?
If your base-types arrays of (int, byte, char, String, JPanel, ...) are of equal length, then you have something like a hidden Object, a database-row. Use a class instead:
class Shoe {
byte size;
String manufactor;
java.math.BigDecimal price;
java.awt.Color color;
}
Shoe [] shoes = new Shoe [7];
If you don't have different types of the same size, they might be unrelated, and should not be put in a common container.
After some testing, I have a simple solution:
Object [][] array = new Object [10][2];
array[0][0] = 2;
array[0][1] = false;
Well, you could define an array of an array of ... an array of Objects (nested with as many levels as dimensions) and at the bottom level fill each array with a different type ... and then, when you need to extract a value, cast it to the appropriate type. Too much work for what is worth, really. Java is no good for this kind of things, being a statically-typed language.
Maybe you should reconsider, why would you need such a data structure.
You can get the effect by using an object array:
final static Object tryit[][] = {
{'a',4},
{'b',7},
{'c',8},
};
#Test
public void accessArray( ) {
for (int i = 0; i < tryit.length ; i++) {
char letter = (Character)tryit[i][0];
int value = (Integer)tryit[i][1];
System.out.println(letter + " has value " + value);
}
}
The "#Test" is JUnit annnotation.
Note this approach would be subject to NullPointer and ClassCast exceptions at runtime if the wrong data is entered in the array.

Assigning a char array to an int array

Can someone explain why the last assignment in the code below is not valid
class ScjpTest extends BounceObject implements Bouncable{
static int ac = 5;
static char ab = 'd';
static int[] a = new int[]{1,2,3};
static int[] b = new int[]{1,2,3};
static char[] c = new char[]{'a','b'};
public static void main(String[] args){
a = b;
b = a;
ac = ab; //This is accepted
a = c; //This is rejected
}
}
The compiler complains with the following error
ScjpTest.java:10: incompatible types
found : char[]
required: int[]
a = c;
^
1 error
The following is also accepted
class Animal{}
class Horse extends Animal{]
Animal[] animals = new Animal[2];
Horse[] horses = new Horses[2];
animals = horses;
Why can i then not assign a char array to an int array?
Because the language specification forbids it. It works at runtime to view a String[] as an Object[] because both have the same representation in memory. But char[] and int[] have different memory representations, and it would be unacceptably slow if every operation on an int[] had to check whether it was really a char[] in disguise.
Arrays of reference type do have to do runtime checks for assignments in order to know whether to throw ArrayStoreException, but at least reads from them is independent of the element type.
The array is a type itself, and casting doesn't work the same way as it primitives. So you have to loop all elements and assign them to the respective element of the other array.
You can assign a char variable to an int one, because the compiler applies a broadening conversion. The basis being that an int is larger than a char and as such there is no chance of loss of information in the conversion. If you tried to assign your int to your char though, you would notice the compiler rejects your code until you put in a specific typecast. This is because narrowing conversions almost always involve a loss of data, and as such the compiler requires you, the programmer, to explicitely indicate this is what you intend to do.
The same principle applies to objects. A broadening conversion is allowed implicitely, a narrowing conversion requires a typecast. Note that at no time can you convert objects that aren't related to each other by super-class/sub-class hierarchy.
Number n = null;
Integer i = null;
n = i ; // Allowed without casting, Number is superclass of Integer
i = n; // Compiler error, Integer is sub-class of Number
i= (Integer)n; // Allowed due to the type-cast
In the example above, Number is a superclass of Integer. Assigning an instance of Integer to instance of Number is allowed without typecast, since this is a 'broadening' conversion. Assigning an instance of Number to instance of Integer requires a specific cast since this is a narrowing conversion (Number could have represented a Float, or some other subclass). These rules carry over to arrays. So, you can do the following:
Number[] numArr = null;
Integer[] intArr = null;
numArr = intArr;
intArr = numArr; //Compile error
intArr = (Integer[]) numArr;
For primitive arrays, there is no broadening conversion done on the array elements, even in cases where you think that it might make sense (ie, char to int, or byte to char etc). While this might not seem to make sense, if you look at the object analogue it becomes clearer why:
Double[] doubleArr = null;
Integer[] intArr = null;
doubleArr = intArr ; // Compile error, Double,Integer are sibling classes
doubleArr = (Double[]) intArr; // Compile error, same reason
intArr = doubleArr; // Compile error, same reason
intArr = (Integer[]) doubleArr; // Compile error, same reason
You can not cast a Double to an Integer, or vice-versa, they are completely different classes from the point of view of the compiler (as different as String and Float would be, for example).

Categories