I'm under the impression that the variable in for-each loop would get the value copied from the elements from the array. E.g.:
String[] strs = new String[] {"a", "b"};
for (String s : strs) {
s = "c";
}
for (String s : strs) {
System.out.println(s);
}
The above code has nothing to do with the original array strs and thus the output is still:
a
b
This is expected as the variable s in the for-each loop is actually a copy of the element. However, if I define my own class along with assignment constructor, the behavior changes:
class Node {
public Node(Integer value, String str) {
value_ = new Integer(value.intValue());
str_ = new String(str.toCharArray());
}
public Node(Node node) {
this(node.value(), node.str());
}
public Integer value() { return value_; }
public String str() { return str_; }
public Integer value_ = 0;
public String str_ = "null";
}
Node[] my_list = new Node[] {new Node(1, "a"), new Node(2, "b")};
for (Node n : my_list) {
n.value_ = 3;
n.str_ = "c";
}
for (Node n : my_list) {
System.out.println(n.value() + " " + n.str());
}
The output now is:
3 c
3 c
which means the variable n in the for-each loop is not a copy of the element but a reference.
Anyone can help explains why the behavior of for-each loop on String and my own class Node is inconsistent?
Thanks!
The value of the variable in the for-each loop is a copy of the values you are iterating on.
Remember that Java has two types of values: primitive values and reference values. Reference values are pointers to objects.
In the first example, s = "c" makes the variable s point to a new object. Since s is a local variable within the loop, there is no effect you can observe from the outside.
In the second example, n.value_ = 3 first finds the object that n points to: remember that n is a reference value, it's a pointer to an object. Then it goes and changes the value of field value_ within that object. These objects exist outside of the loop, so this change can be seen from the outside.
See also the very similar discussion on how the reference vs value semantics affect values passed to methods at Is Java "pass-by-reference" or "pass-by-value"?
In the first example, as you rightly pointed out, you are iterating through the str array and then setting the value to c. You are however not setting the value 3 to the str array. You can set the value to c by making the below changes to your first example.
`for (int i = 0;i<strs.length;i++) {
strs[i] = "c";
}`
This in effect is changing the value of the str array.
However in the second example you are making changes to the variables of the object passed in the for loop. Therefore the variables inside the object changed (in effect, you set the value to the objects variable)
Related
I'm a beginner in Java I have some questions regarding the enhanced for loop and string arrays.
In the source code below, I've been trying to change the contents of the arrays using the enhanced-for loops.
It seems that I can't change the contents in the the String array(arrString), but I don't know exactly why. (It works fine for StringBuilder objects.)
I am a bit confused because I could actually do operations like str1+=str2 and change the String contents (although this operation is done via StringBuilder class) in normal situations.
Can anyone point out why this is happening and if there's any misunderstanding on my part?
class EnhancedForTest{
public static void main(String[] args){
//StringBuilder
StringBuilder[] arrStringBuilder=new StringBuilder[]{new StringBuilder("Hello1"), new
StringBuilder("Hello2"),new StringBuilder("Hello3")};
for(StringBuilder e: arrStringBuilder){
e.append("!");
}
for(StringBuilder e:arrStringBuilder){
System.out.println(e);
}
//String
String[] arrString=new String[]{"Hello1","Hello2","Hello3"};
for(String s:arrString){
s+="!";
}
for(String s:arrString){
System.out.println(s);
}
}
}
The results are as follows(Sorry I didn't add the results!):
Hello1!
Hello2!
Hello3!
Hello1
Hello2
Hello3
P.S.: I've taken out the printing line as a new for loop, it still works for StringBuilder but not String
On each iteration of the for-each loop the variable obtains a reference to the corresponding element of the array. In the case of for(StringBuilder e:arrStringBuilder) on the first iteration a new variable e of type StringBuilder will point to the same object as arrStringBuilder[0]. Then you call append using this reference, which performs operations on the object pointed by both references e and arrStringBuilder[0]. On the next iteration e will be assigned reference to arrStringBuilder[1], but the changes made in arrStringBuilder[0] will stay.
In the case of for(String s:arrString) on the first iteration a new variable s will be created pointing to arrString[0]. However, when you do s += '!' you actually perform s = s + '!', so you assign to this temporary variable s a reference to the new String object, which will contain Hello1!. This new object, however, is not related in any way to the original arrString[0] object, and at the start of the next iteration is just discarded. Your loop works like this:
for(int i = 0; i < 3; i++) {
// Create a temporary variable which points to the same object as arrString[i]
String s = arrString[i];
// Create a temporary object which keeps the result of concatenation of s and '!'
String temporary = s + '!';
// Replace the reference stored in s with the reference to the temporary object
s = temporary;
// Now there is no connection between s and arrString[i]
// And here we just discard both temporary objects 's' and 'temporary'
// arrString[i] object remains unchanged.
}
e.append("!"); is a call to a method, whose specific purpose is to modify e.
s+="!"; is not a method call. It is exactly equivalent to this:
s = s + "!";
This does not alter the state of any object. It merely creates a new String object (that is, a String whose text value is the old value of s plus "!"), and assigns that to the variable s. You are changing what s holds, but you didn’t modify the original String object that s used to hold.
It turns out there are no methods of String which will modify the String object, because Strings are immutable, by design. On the other hand, the StringBuilder class exists specifically to create and work with changeable text values.
In this Trie implementation, children array elements are assigned null value individually using a for loop.
TrieNode(){
isEndOfWord = false;
for (int i = 0; i < ALPHABET_SIZE; i++)
children[i] = null;
}
However, by default, when we create an array of reference types in Java, all entries will have default value as null which is:
TrieNode[] children = new TrieNode[ALPHABET_SIZE];
The above step assigns default values of children array entries as null.
Is it required to have null assignment once again in the for loop inside that TrieNode constructor?
No it's not required - for each class variable, instance variable, or array component Java will always assign reasonable default value (like 0 for int or null for Object) - you can read more here
However notice that for local variables it's not guaranteed
The compiler will assign a reasonable default value for fields of the above types; for local variables, a default value is never assigned.
and that's why you are forced to initialize it manually
public void f() {
String s;
System.out.println(s); // will cause Error: java: variable s might not have been initialized
}
I understand that on passing an array asn argument to a function, and making some change to the elements of the array inside the function, the changes will be reflected in the calling function as well, since arrays operates directly on memory (call by reference)
However, why is it that the same behavior does not apply to Strings? I was expecting Strings as well to work in the same way since a String is basically an array of characters.
Please see my code below.
I pass in a String (character array) as well as an int array in to a function and make some changes in it. On printing these in main, I see that the String remains unaffected whereas the changes to the array are reflected.
import java.util.Arrays;
public class TestString
{
public static void main(String[] args)
{
String s = "hello";
int[] a = new int[5];
Arrays.fill(a, -1);
fun(s,a);
System.out.println(s);
System.out.println(a[0]);
}
static void fun(String s, int[] a)
{
s = "world";
a[0] = 99;
}
}
Output
hello
99
First, the claim that a String is an array of characters is wrong. A String is an object, that has methods and is designed specifically not to allow any changes to be made in it.
Second, what you are doing is not changing some element of the parameter. You are changing the parameter variable. The parameter variable is basically a local variable, which receives a reference to the string that was passed as argument. Doing this:
s = "world";
Is not changing the string that was passed. It replaces contents of the local variable s with a new string. Since Java is always pass by value, this is not reflected outside of the method. It would be the same if you had:
a = new int[30];
inside the method. Outside of it you would still see the 5-element int[] that you passed inside.
a[0] = 99;
Is changing an element inside the array. So it looks at a, checks what it refers to, goes to that referred array, and changes its 0th element.
Since String is designed to be immutable, there is no way to do something similar in it. You can't do something like:
s.setCharacter(0,'a'); // This doesn't exist in Java
But you can do this with mutable objects. For example, if you had:
public static void manipulateStringBuilder( StringBuilder sb ) {
sb.append(": manipulated");
sb = new StringBuilder("New value assigned");
}
Then you could write something like:
StringBuilder sb = new StringBuilder("My string");
System.out.println(sb);
manipulateStringBuilder( sb );
System.out.println(sb);
And the output would be:
My string
My string: manipulated
This is because StringBuilder is a mutable object, combined with the fact that the value that you assigned to sb inside your method is never seen outside of it.
Because Strings in java are immutable. Every "change" done to a string does not changes the original string but creates a new String object.
It is because Strings are immutable. If you want to change the value of the String you should return it from the method and store it in the variable s again.
import java.util.Arrays;
public class TestString
{
public static void main(String[] args)
{
String s = "hello";
int[] a = new int[5];
Arrays.fill(a, -1);
s = fun(s,a);
System.out.println(s);
System.out.println(a[0]);
}
static String fun(String s, int[] a)
{
s = "world";
a[0] = 99;
return s;
}
}
I expect it will be helpful for you!
Java is not call by reference, but call by value, it is just that it passes references as values. More details here.
Strings are immutable, so you can't change the content (the char array) of a String, you can just replace it with a different one, i.e. you are changing the reference. But since the reference is passed by value, the change is not visible outside a method.
Actually you can change the content of String using reflection, which results in very interesting behavior. You use this only when you want to play a prank on a coworker.
public void setData(double[] d) {
if (d == null) {
data = new double[0];
} else {
data = new double[d.length];
for (int i = 0; i < d.length; i++)
data[i] = d[i];
}
}
this method in my code is used to set the data of an array. I am also required to write a method called reset() that changes a given array to have a null value. Also, we are practicing overloading in this lab. There are four versions of setData() (double, int, float, long). Since a double array is used internally by the Stat class to store the values, do I only have to make one reset() method of type double?(I think I only need one...) Finally, please give me some hints as to going about this reset business because everything I have tried has failed miserably and usually consists of statements such as
"setData(double[] null)" which return errors.
Everything in java is pass by value; even references are passed by value. So by passing an array through a method, you can change the contents of the array, but you cannot change what the array points to. Now, if you are inside a class and happen to pass an instance member that you already have access to by virtue of being in the class, you will be able to set the array to null.
If you always want to be able to change what an array points to, then simply have a function which returns an array (instead of being void), and assign that returned value to the array of interest.
Because java is pass by value, you can't reassign a variable passed as a parameter to a method, and expect to see that change reflected outside.
What you can do, is put the array in some sort of wrapper class like this:
class ArrayReference<T> {
T[] array; // T would be either Double, or Long, or Integer, or whatever
}
and then:
void setData(ArrayReference<Double> myReference) {
myReference.array = null;
}
I'm not sure if I understood your question, but is it that what you want?
public class Stat {
private double[] data;
public void reset() {
data = null;
}
public void setData(double[] d) {
data = (d == null) ? new double[0] : Arrays.copyOf(d, d.length);
}
}
Basically I have a variable, zlort = one;
I want to concatenate the value of zlort into a variable (object reference) name.
Like
BankAccount Accountzlort = new BankAccount;
I want the zlort in Account.zlort to actually be the replaced with value of zlort (one--meaning I want the value to be Accountone), and not zlort itself.
Is it possible to do this?
Thanks!
No you can't, but you might put the instance in a map:
Map<String,BankAccount> map = new HashMap<String,BankAccount>();
map.put("Account" + zlort, new BankAccount());
If you mean dynamically choosing the name to assign a variable to, then no.
You could use a HashMap to achieve the same effect.
It is not possible to change the name of a variable at runtime. That would lead to extreme security and stability problems when dealing with any real-world application.
However, as the two answers here have mentioned, a HashMap might acheive what you are looking for. (See the javadoc!!)
A HashMap (or any other map, for that matter) maps a Key to a Value. The concept is similar to a variable, which is a name -> value mapping. The only difference is that variables are part of the actual program code, which is effectively unmodifiable after compiling. A Map is a data structure that can be modified by the running program. This allows you to freely add key-value pairings to it.
Note that in Java, type-safety is encouraged through the use of Generics. Basically this ensures that the key can only be of one type (e.g. String) and the value can be of only one type (BankAccount). A thorough coverage of Generics can be found here.
You would declare this as follows:
Map<String, BankAccount> accounts = new HashMap<String, BankAccount>();
And then to add a key-value pair to the map, you would use the put() method (which 'puts' a value into the map, associated with a key)
String key = "Key"
BankAccount value = new BankAccount();
accounts.put(key, value);
To retrieve it, you would use the get() method.
BankAccount retrievedValue;
retrievedValue = accounts.get(key);
After reading the explanations in your comments, the fact that you can't use an array but can use an `ArrayList'...
Rather than creating a new variable name (or array element, or map value) for each BankAccount, you can probably use scope to your advantage.
Scope is the concept that a reference to a variable only has meaning within a certain part of code. If you declare a variable inside a method, that variable can only be seen within that method. A variable declared within a block (a loop, if statement, etc ) can only be seen from within that block.
Class fields have a different kind of scoping that can be adjusted with keywords (see here).
For example:
public class ScopeExample
int classInt = 10;
public void method() {
int methodInt = 0; // This integer can only be seen by code in
// this method
}
public void method2() {
//doSomething(methodInt) // This line won't compile because i is
// declared in a different method!
doSomething(classInt); // This line will compile and work
// because x is declared in the class that
// contains this method.
int index = 0;
while (index < 3) {
int whileInt = index; // This integer can only be seen from within
// this while loop! It is created each
// loop iteration.
doSomething(whileInt);
}
doSomething(whileInt); //This line won't work, whileInt is out of scope!
}
public doSomething(int a) {
System.out.println(a);
}
}
SO! If you create a BankAccount object within the loop, you don't have to worry about creating a new name for the next one. Each time the loop iterates it will become a new object (when you create it).
If you have to store it, you definitely will need to use an array or other data structure (ArrayList!).
Building on the idea of scope, you -can- have the same variable name for each new BankAccount. A variable reference name isn't guaranteed to be paired with the object that it refers to. That is a convenience to the programmer, so you don't have to know the exact memory address it is being stored in.
For example:
public static void main(String[] args) {
Object o;
int i = 0;
while (i < 5) {
Object reference = new Object(); // Create a new Object and store
// it in 'reference'
o = obj; // The Object 'o' now refers to the object in 'reference'
i++;
}
System.out.println(o); // This should print information about the
// LAST object created.
}
The new Object created in the loop does not belong to 'obj'. You as a programmer use 'obj' to point to the Object. The program doesn't really know what obj means, other than the fact that it points to the Object you just created.
Finally, you can use this along with an ArrayList to make your life easier.
public static void main(String[] args) {
// Our new ArrayList to hold our objects!
ArrayList<Object> stuff = new ArrayList<Object>();
int i = 0;
while (i < 5) {
Object obj = new Object(); // Create an object and make obj point to it.
stuff.add(obj); // Put "the object that 'obj' points to" in 'stuff'.
i++;
}
// This loop goes through all of the Objects in the ArrayList and prints them
for (int index = 0; index < stuff.size(); index++) {
System.out.println(stuff.get(i)); // This will print a single
// object in the ArrayList each time.
}
}