This is my first question at StackOverflow. This is the code in question:
public class ListStuff {
public static void main(String [] args) {
String[] randomNames = {"Herbie", "Jaco", "Pat", "Michael"};
String[] reversedNames = revertNames(randomNames);
for (int i = 0; i < reversedNames.length; i++) {
System.out.println(reversedNames[i]);
}
}
public static String[] revertNames(String[] s) {
for (int i = 0; i < s.length / 2; i++) {
String tmp = s[s.length - 1 - i];
s[s.length - 1 - i] = s[i];
s[i] = tmp;
}
return s;
}
}
This code runs fine and the reversedNames variable prints as reverted; no complaints there. My main concern, however, is that when I do the String[] reversedNames = revertNames(randomNames);, the variable randomNames also gets reverted. I do in no place change the randomNames variable with a randomNames = blabla;, so I fail to see why that variable keeps changing into a reverted version of itself, even though I am only passing it as an argument.
I have been programming for approximately one year, and my knowledge of variable scopes and such is very limited. Can anyone point me in the right direction?
Arrays in Java are reference types. This means that when you pass your array to the revertNames method, any change inside that method will also be seen outside. Since you are changing the array parameter s inside revertNames with this code:
String tmp = s[s.length - 1 - i];
s[s.length - 1 - i] = s[i];
s[i] = tmp;
the original array randomNames that was passed in place of s is also changed in the process.
That is because you are passing the array by reference, rather than by value. Essentially, this means that the reversedNames array still points to the randomNames array, so changing one will change the other.
Here is a diagram of the variables:
Initially:
randomNames
As we enter the revertNames function:
randomNames <-------- s
The s array still points back to the randomNames array! Thus, when we change s, we change randomNames as well.
As we leave the function:
randomNames <--------- s <--------- reversedNames
Thus, reversedNames points to randomNames.
After the function is finished calling:
randomNames <--------- reversedNames
Although the s array has disappeared, reversedNames still points to randomNames.
To fix the issue, create a temporary variable inside the revertNames function:
public static String[] revertNames(String[] oldarray) {
// Create temporary array to avoid affecting original array
String[] s = oldarray.clone();
...
Within the body of revertNames you have a variable s, this is a reference to an object which is an array. The actual array is the array randomNames. So you are indeed changing values in the source array.
If you use Arrays.copy() you can get an indepedent array to work in - the copy will point to the same strings as the original array, but as Strings are immuatable that's safe.
You can use : StringBuffer:reverse()
some thing like that :
public class t {
public static void main(String [] args) {
String[] randomNames = {"Herbie", "Jaco", "Pat", "Michael"};
StringBuffer rev;
for (int i = 0; i < randomNames.length; i++)
{
rev=new StringBuffer(randomNames[i]);
System.out.println(rev.reverse().toString());
}
}
}
Arrays in Java are reference types, i.e. if you declare
String[] randomNames;
you have declared a local variable that holds a reference to an object that is an array of strings.
The statement
String[] otherNames = randomNames;
will copy the content of variable randomNames to the variable otherNames, but the content is just a reference. That is, this statement causes both otherNames and randomNames to refer to the same array object.
The same happens when you pass a String[] as method parameter. That is, the local variable s in revertNames, and the local variable randomNames in main will contain identical references, i.e. refer to the same array object. That is, the state of that array object will be visible though both s and randomNames.
Strictly speaking, this has nothing to do with scoping, because the variable randomNames is not modified (i.e. it still points to the same array). What is modified is the object it refers to.
Related
I am confused why the hello method is able to affect the array declared in the main method. My understanding was it was outside of the scope.
public class Test {
void hello(String i, int[] arr){
i += "hello";
arr[0] = 4;
}
public static void main(String args[]){
Test t = new Test();
String i = "hey";
int [] arr = {0,1,2,3};
t.hello(i,arr);
System.out.println(i);
System.out.println(Arrays.toString(arr));
}
}
returns
hey
[4, 1, 2, 3]
i would understand a return of "heyhello" or [0,1,2,3].
There's an important difference between String and int[].
Both are so-called "reference objects", meaning that they hava a unique identity, and can be referenced from variables, fields, method arguments etc.
So, t.hello(i,arr) passes two arguments into the hello() method:
a reference to a String with content "hey"
a reference to an int[] array with content {0,1,2,3}.
Inside hello(), you "modify" both variables, but differently:
With i += "hello";, you append a word to the end of the string. The Java designers decided to make strings "immutable", so you can't change a String object that has been created, you can only create a new string in such a case. That means that the original string (that the i variable from main() still references) will not be affected. It's only the i inside hello() that now references the fresh, longer string. And main() still has its i being "hey".
With arr[0] = 4;, you set the first array element to a different value. Arrays have been defined differently, they are mutable. For an existing array, you can modify its elements without the need for a fresh copy. So, there's still only one array in use, and its first element has been modified. When main() prints its arr array, it is the identical array that has been modified inside hello(), und thus contains the number 4.
The posted code is demonstrating the difference between passing mutable objects and immutable objects as parameters.
The array arr is passed to hello as an argument. That means the pointer to the arr object is copied and the copy is given to hello, so hello has a pointer to the array declared in the main method. Arrays are mutable and hello has a reference to it,, nothing stops hello from changing the contents of arr.
The string i gets passed into hello similarly, hello gets a copy of the pointer. The hello method replaces the string referred to by its copy of the pointer (since strings are immutable), but the pointer used by the main method is unaffected and still points to the original value of i.
I am going to ask a basic question about Java memory usage.
Imagine we have an array List and it is large enough and we don't like to use more memory. Now if I want to pass this array to another methods in this class, or other classes through their constructor or method, do I need additional memory/is there additional memory usage for this array?
If yes, could I just make this array package level, and therefore the other classes in this package could access it directly, without any memory need.
Thank you in advance.
No, no additional memory is necessary. The parameter of a function is passed by copy of the reference. It means that for any kind of object only 4 additional bytes are used.
If you pass an array as parameter and you modify it in the body of the method the changes will be exported outside of method.
Instead if you reassign the array variable, the difference is not visible externally.
This happens because the parameters are passed as copy of the reference and not by reference.
public void vsibleModification(int[] a) {
for (int i = 0; i < a.length; i++) {
// This change is visible outside of method because I change
// the content of a, not the reference
a[i] = a[i] + 1;
}
}
public void nonVisibleModification(int[] a) {
// Non visible modification because a is reassigned to a new value (reference modification)
a = new int[2];
a[0] = 1;
a[1] = 2;
}
The following code in Java uses a final array of String.
final public class Main {
public static final String[] CONSTANT_ARRAY = {"I", "can", "never", "change"};
public static void main(String[] args) {
for (int x = 0; x < CONSTANT_ARRAY.length; x++) {
System.out.print(CONSTANT_ARRAY[x] + " ");
}
}
}
It displays the following output on the console.
I can never change
If we try to reassign the declared final array of type String, we cause an error:
final public class Main {
public static final String[] CONSTANT_ARRAY = {"I", "can", "never", "change"};
public static void main(String[] args) {
CONSTANT_ARRAY={"I", "can", "never", "change"}; //Error - can not assign to final variable CONSTANT_ARRAY.
for (int x = 0; x < CONSTANT_ARRAY.length; x++) {
System.out.print(CONSTANT_ARRAY[x] + " ");
}
}
}
Error: cannot assign to final variable CONSTANT_ARRAY.
However, the following code works:
final public class Main {
public static final String[] CONSTANT_ARRAY = {"I", "can", "never", "change"};
public static void main(String[] args) {
CONSTANT_ARRAY[2] = "always"; //Compiles fine.
for (int x = 0; x < CONSTANT_ARRAY.length; x++) {
System.out.print(CONSTANT_ARRAY[x] + " ");
}
}
}
It displays
I can always change
This mean that we could manage to modify the value of the final array of type String. Can we modify the entire array in this way without violating the immutable rule of final?
final in Java affects the variable, it has nothing to do with the object you are assigning to it.
final String[] myArray = { "hi", "there" };
myArray = anotherArray; // Error, you can't do that. myArray is final
myArray[0] = "over"; // perfectly fine, final has nothing to do with it
Edit to add from comments: Note that I said object you are assigning to it. In Java an array is an object. This same thing applies to any other object:
final List<String> myList = new ArrayList<String>():
myList = anotherList; // error, you can't do that
myList.add("Hi there!"); // perfectly fine.
You are misinterpreting the final implementation. final applies to the array object reference, which means once it is initiated, the reference can never change but the array its self can be populated. "Its not violating the rules" you have specified only one rule about the reference change which is working accordingly. If you want the values should also never change you should go for Immutable lists i.e
List<String> items = Collections.unmodifiableList(Arrays.asList("I", "can", "never", "change"));
You can only make it so the array reference can't be changed. If you want the elements to be unable to be changed, you need to use an unmodifiable collection of some kind.
When you declare an array as final, you can change the elements in the array, however you cannot change the reference of this array.
final only guarantees immutability of primitives. And also guarantees that a variable is assigned only once. If an object is mutable you can change the content of it event it defined as final. You may check immutable collections for your needs. Such as Collections.unmodifiableList()
http://docs.oracle.com/javase/6/docs/api/java/util/Collections.html#unmodifiableList(java.util.List)
The reference to the array object is final (can not change e.g. in case you would attempt to associate a different Java array object (instance of String[]) to the same final variable...you'd get a compile time error).
BUT the fields of the final array object in your example are not final, and so you can modify their value. ...while the Java object you created, CONSTANT_ARRAY, after receiving an initial value, will have that value "forever" == until the JVM stops. :) It will be the same String Array instance "forever".
Final variables in Java are not a big deal, just spend some time to digest the topic/idea carefully. :-)
I suggest to all of those who are uncertain to meditate over this page, for example:
https://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.4
Let me cite the respective part:
"Once a final variable has been assigned, it always contains the same value. If a final variable holds a reference to an object, then the state of the object may be changed by operations on the object, but the variable will always refer to the same object.
This applies also to arrays, because arrays are objects; if a final variable holds a reference to an array, then the components of the array may be changed by operations on the array, but the variable will always refer to the same array."
The value of the variable CONSTANT_ARRAY cannot change. That variable contains a (reference to an) array. However, the contents of the array can change. Same thing happens when you declare any kind of final variable that is not a simple scalar type (e.g. an object).
Be careful how you name your variables. :-) Calling it a CONSTANT_ARRAY doesn't make the contents of the array unchangeable.
Here's a good reference: The final word on final
When a variable is declared with the final keyword, its value can’t be modified, essentially, a constant. This also means that you must initialize a final variable. If the final variable is a reference, this means that the variable cannot be re-bound to reference another object, but the internal state of the object pointed by that reference variable can be changed i.e. you can add or remove elements from the final array or final collection.
final int[] res;
int[] res1;
int[] res2 = new int[1];
res2[0]=20;
res1=res2;
res1=res2;//no error
System.out.println("res1:"+res1[0]);
res = res2;//only once
//res = res2;//error already initialised
res2[0]=30;
System.out.println("res:"+res[0]);
output::
res1:20
res:30
This question already has answers here:
Changing array in method changes array outside [duplicate]
(2 answers)
Closed 3 years ago.
public class Test {
public static void main(String[] args) {
int[] arr = new int[5];
arr[0] = 1;
method(arr);
System.out.println(arr[0]);
}
private static void method(int[] array)
{
array[0] = 2;
}
}
After invoking method, arr[0] becomes 2. Why is that!?
You can call set methods on objects passed to a method. Java is pass by value, which means that you can't replace an object in a method, though you can call set methods on an object.
If Java were pass by reference, this would pass:
public class Test {
public static void main(String[] args) {
Test test = new Test();
int j = 0;
test.setToOne(j);
assert j == 1;
}
public void setToOne(int i) {
i = 1;
}
}
Java is Pass-by-Value, Dammit! http://javadude.com/articles/passbyvalue.htm
This is because Java uses Call by Object-Sharing* (for non-primitive types) when passing arguments to method.
When you pass an object -- including arrays -- you pass the object itself. A copy is not created.
If you mutate the object in one place, such as in the called method, you mutate the object everywhere! (Because an object is itself :-)
Here is the code above, annotated:
public static void main(String[] args)
{
int[] arr = new int[5]; // create an array object. let's call it JIM.
// arr evaluates to the object JIM, so sets JIM[0] = 1
arr[0] = 1;
System.out.println(arr[0]); // 1
method(arr); // fixed typo :-)
// arr still evalutes to JIM
// so this will print 2, as we "mutated" JIM in method called above
System.out.println(arr[0]); // 2
}
private static void method(int[] array)
{
// array evaluates to the object JIM, so sets JIM[0] = 2
// it is the same JIM object
array[0] = 2;
}
Happy coding.
*Primitive values always have call-by-value semantics -- that is, a copy is effectively created. Since all primitive values are immutable this does not create a conflict.
Also, as Brian Roach points out, the JVM only implements call-by-value internally: the call-by-object-sharing semantics discussed above are implemented by passing the value of the reference for a given object. As noted in the linked wikipedia article, the specific terms used to describe this behavior differ by programming community.
Additional:
Pass by value or Pass by reference in Java? -- see aioobes answer and how it relates with Brian Roachs comments. And aioobe again: Does array changes in method?
Make copy of array Java -- note this only creates a "shallow" copy.
Because that's exactly what you're telling it to do. Java passes first by value, then by reference. You're passing in the array, but any modifications you make to that array will be reflected on any other accesses to that array.
A quick example for thought:
If within method you did array = null, no change would be visible from main - as you would be changing the local value of array without modifying anything on the reference.
Because when you are passing argument like int/double/char etc. they are the primitive data types and they are call by value - meaning their values are copied to a local variable in this method (that has the same name as the names in your argument) and changes made to them are only changes made to these local var -> does not affect your primitive type variables outside the method
however an array or any object data type is called by reference -> the argument pass their address(reference) to method. that way, you still have a local variable named by them which has the reference. You can change it to reference another array or anything. but when it is referencing an array, you can use it to set the value of the array. what it does is accessing the address it is referencing and change the content of the referenced place
method(arr[0]);
I think that's supposed to be
method(arr);
But anyway the value passed as argument to the method is the reference to the array and the local variable arr in the method is referencing the same array. So, within the method you are making changes to the same array.
Java is pass by value. What confuses people is that the 'value' of a variable that refers to an object allocated on the heap is a reference, so when you pass that, you pass the reference 'by value' and therefore it refers to the same object on the heap. Which means it doesn't matter from where you modify the referent; you're modifying the same thing.
I was wondering, in java, is it possible to in anyway, simulate pass by reference for an array? Yes, I know the language doesn't support it, but is there anyway I can do it. Say, for example, I want to create a method that reverses the order of all the elements in an array. (I know that this code snippet isn't the best example, as there is a better algorithms to do this, but this is a good example of the type of thing I want to do for more complex problems).
Currently, I need to make a class like this:
public static void reverse(Object[] arr) {
Object[] tmpArr = new Object[arr.length];
count = arr.length - 1;
for(Object i : arr)
tmpArr[count--] = i;
// I would like to do arr = tmpArr, but that will only make the shallow
// reference tmpArr, I would like to actually change the pointer they passed in
// Not just the values in the array, so I have to do this:
for(Object i : tmpArr)
arr[count++] = i;
return;
}
Yes, I know that I could just swap the values until I get to the middle, and it would be much more efficient, but for other, more complex purposes, is there anyway that I can manipulate the actual pointer?
Again, thank you.
is there anyway that I can manipulate the actual pointer?
Java does not pass by reference, so you can't directly manipulate the original pointer. As you've found out, Java passes everything by value. You can't pass a reference to an array object, and expect a method to modify the original reference to point to another array object.
You can, of course:
Modify elements of the referred array object (ala java.util.Arrays.sort)
Pass a reference to an object with a settable field (e.g. Throwable has a setStackTrace)
return the new reference instead (ala java.util.Arrays.copyOf)
Well, you can explicitly pass an object that contains a reference. java.util.concurrent.atomic.AtomicReference is ready out of the box, although it does come with volatile semantics that you probably don't want. Some people use single element arrays to returns values from anonymous inner classes (although that doesn't seem a great idea to me).
This method reverses the Array's elements in place. The caller sees the changes. (In Java everything is passed by value, including object references.)
public static void reverse(Object[] arr) {
for ( int i = 0, j = arr.length - 1; i < j; i++, j-- ) {
Object temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
In Java Object reference is passed by value.
So if you looking for something like
function referenceCheck()
{
int[] array = new int[]{10, 20, 30};
reassignArray(&array);
//Now array should contain 1,2,3,4,5
}
function reassignArray(int **array)
{
int *array = new int[] { 1, 2, 3, 4, 5};
}
Then its not possible in Java by any direct means.
If we need to change only the values stored in an array, then we can do it since object reference is passed by value.
You want to pass a reference to the array reference. In that case you just have to either create a class to hold the reference and pass a reference to that class or just pass a 1-element array of the type being passed. Then you'd be passing either an object holding the array or an array whose only element contains the array you want to operate on.