I have the following code
List<String> strings = new ArrayList<String>();
strings.add("a");
strings.add("b");
strings.add("c");
for (String s : strings) {
s = new String("x");
}
System.err.println(strings);
which prints [a, b, c]
I thought it would print [x, x, x] because I iterate over the Strings, which returns a reference to a String Object. In the loop I assign a new Object to the reference, accordingly s should point to the new String Object?
Where is my fault?
What is the best way to update the strings in my array?
In the loop I assign a new Object to the reference
Well, you assign a new reference as the value for s. s is just a local variable though, which was initialized with the value of the element. It's not tied to the element in the list though - that was just how it was initialized. It's a bit like changing the value of a method parameter - that doesn't change whatever variable was used as an argument:
void method(String y) {
y = "bar"; // This doesn't change x.
}
...
String x = "foo";
method(x);
System.out.println(x); // foo
If you want to update the strings in your list (not an array - it's worth being clear about the difference) you should use a regular for loop:
for (int i = 0; i < strings.size(); i++) {
strings.set(i, "x");
}
It prints "a b c " because you are not changing(adding) anything in the list.
for (String s : strings) {
s = new String("x");
}
The above code can be read as :
For each String s in List strings set s to a new String value "x". You are not doing anything to the list. You get the value from the list, store it in s and overwrite s.
You only change the value of the local s variable, not the elements in the List.
You can change elements in the list by List.set(int index, E element).
s you specified here has scope only in the for loop
for (String s : strings) {
s = new String("x");
}
value of new String object is passed to s on each iteration, but strings is not getting affected at all.
Related
I have used some other answers to get a solution to my problem. But I am wondering if there is a way to improve this further?
// Copy the masterList ArrayList and then sort in ascending order and then make a third
// ArrayList and loop through to add the 8 lowest values to this list.
ArrayList<Integer> sortedList = new ArrayList<>(Calculator.masterList);
Collections.sort(sortedList);
ArrayList<Integer> lowEight = new ArrayList<>();
for (int i = 0; i < 8; i++) {
lowEight.add(sortedList.get(i));
}
// Set TextView as the value of index 0 in masterList ArrayList, check if lowEight
// ArrayList contains the element that is the same as masterList index 0 and if
// so highlight s1 textview green.
s1.setText("Score 1 is " + String.format("%d", Calculator.masterList.get(0)));
if (lowEight.contains(Calculator.masterList.get(0))) {
s1.setBackgroundColor(Color.GREEN);
}
This works to an extent by highlighting the values that are in both masterList and lowEight but for example if the number 7 is in lowEight and appears 9 times in masterList it will highlight all 9 occurences. Is there a way to move the exact object from masterList to sortedList and then to lowEight and then a method to check the object and not just the value?
Let me provide a more concise example of what you're asking. Let us take the following code:
ArrayList<Integer> list1 = new ArrayList<Integer>() {
{
add(5);
add(5);
}
};
ArrayList<Integer> list2 = new ArrayList<>();
list2.add(list1.get(0));
list1.forEach((i) -> System.out.println(list2.contains(i)));
The output is:
true
true
But you would expect it to be:
true
false
Because the first and second element are different objects. The problem here is that although they are different objects, they are equal objects. The way the Integer class is written in Java, any Integer is equal to another Integer if they represent the same value. When you run the contains() method, it sees that the list does indeed contain an object equal to the one you provided (in this case they both represent a value of 5), and so it returns true. So how do we solve this problem? How do we tell one Integer object from another? I would write your own "Integer" class. Something like "MyInteger". Here's a very simple implementation you could use:
public class MyInteger {
private final int i;
public MyInteger(int i) {
this.i = i;
}
public int toInt() {
return i;
}
}
And then when we use it in our ArrayList code:
ArrayList<MyInteger> list1 = new ArrayList<MyInteger>() {
{
add(new MyInteger(5));
add(new MyInteger(5));
}
};
ArrayList<MyInteger> list2 = new ArrayList<>();
list2.add(list1.get(0));
list1.forEach((i) -> System.out.println(list2.contains(i)));
We get our expected output:
true
false
This works because our new MyInteger class implicitly uses the default equals() method, which always returns false. In other words, no two MyInteger objects are ever equal. You can apply this same principle to your code.
Why for-each loop in java can't be used for assignments?
For eg I am trying the below example and not getting the expected result but compiles successfully:
int count = 0;
String[] obj = new String[3];
for (String ob : obj )
ob = new String("obj" + count++);
System.out.println(obj[0]); // null
ob is a local variable which is a copy of the reference in the array. You can alter it but it doesn't alter the array or collection it comes from.
Essentially, that is correct. The for-each loop is not usable for loops where you need to replace elements in a list or array as you traverse it
String[] obj = { "obj1", "obj2", "obj3" };
or
for (int count = 0; count < obj.length; count++) {
obj[count] = "obj" + count;
}
As you've noted, you can't use the variable in an array iteration to set the values of the array. In fact your code, while legal, is unusual in iterating through the elements of an array in order to initialise them. As other answers have noted, you are better off using the index of the array.
Even better is to create the array in the process of initialisation. For example, in Java 8 you could use:
String[] obj = IntStream.range(0, 4).mapToObj(n -> "obj" + n).toArray();
This seems to me to capture your intent of creating strings and then turning them into a new array rather than create an array and then iterate through it changing each element.
What's difference between
String [] O = {};
and
String[] Ox = new String[3000];
How can I copy the strings from Ox to O?
O is empty array and Ox has 3000 length, to copy Ox to O you have to use copyOf() api of Arrays class.
O = Arrays.copyOf(Ox, Ox.length);
Arrays.copyOf() create change the O array length to 3000 and copy all contents.
public static void main(String[] args) {
String [] O = {};
String[] Ox = new String[3000];
O = Arrays.copyOf(Ox, Ox.length);
System.out.println("O array length : "+O.length); // I am just printing length
}
Output :
O array length : 3000
Internal implementation of copyOf() api of Arrays class.
public static char[] copyOf(char[] original, int newLength) {
char[] copy = new char[newLength];
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
Array is immutable so you can not assign 3000 size array to size 0 array, in implementation of copyOf() method java created new array char[] copy and using native method copy content of original array to copy array.
So, in you code you can not directly copy Ox array contents to O array for that you have to create array of size 3000 or use copyOf() api of Java.
In Java arrays are not mutable, thus you can not change the size of an array. Because you declare O as size 0 implicitly (the {} means its empty) it can not be filled.
You need to declare a new array of size 3000 (new String[3000]) and then use that
O is an empty array, so you can't copy anything to it.
With the following statement you declare a table O that is empty (size=0, no elements).
String[] o = {};
If instead you write:
String[] o = {"this", "is", "a", "test"};
you have initialized table O to have size=4 and contain the four string elements "this", "is", "a" and "test".
Finally, if you initialize your table in the following way:
String[] o = new String[4];
you have declared that your array has size=4, but all four elements are null. Of course, you can later change the values of your array (eg. o[3] = "test").
Now, if you want to copy the elements of one array to the other you can use the Arrays.copyOf(o, o.length); method, but in the following way :
String[] o = {"this", "is", "a", "test"};
String[] copy; // you don't have to (and shouldn't) initialize this variable to {}
copy = Arrays.copyOf(o, o.length);
There are a couple of differences here:
1) String [] O = {};
this means to create a new String[] called O which is initialized with the data set contained in {}. Since there is no data, O is "empty".
This means that O.length = 0, or put another way, this Array has no memory associated with it for storage purposes.
Since Arrays are immutable in java, you cannot do anything further with this array.
You could have done:
String[] O = {"bob","Cat","ralph"}
O.length // == 3
O[0] // bob
O[1] // Cat
O[2] // ralph
O[1] = "Sting"
O[1] // now set to Sting
O[4] = "Cause an Exception" // causes an index out of bounds exception
2) String[] O = new String[3000];
In this case you have created a new String[] with 3000 "spaces" or "indices" available. What this really means is that you can maintain references to 3000 String objects in this array.
So:
O.length; //3000 because that is the length of this array
O[0] // will return null since we have not stored anything here
O[0] = "My String"
O[0] // now equals "My String"
O[3001] // throws an index out of bounds exception
3) How can you copy between them:
In short you cannot in this case. Since array 1 (String[] O = {}) is empty and has a length of 0 any attempt to copy to or from this array will result in an exception.
Thanks to all - brings some new Information for me -
but
O = Arrays.copyOf(Ox, Ox.length);
from atish shimpi
helps to solve the Problem :))
Just found this SO question that happened to solve my problem with initializing a Boolean array initializing a boolean array in java. However, while it gave me code that will work, the asker wasn't trying the code that I was running that wasn't working, and I'd actually like to know why it doesn't work. This was the code I was trying:
Boolean[] array = new Boolean[5];
for(Boolean value : array) {
value = false;
}
This is the functional code from that other question:
Boolean[] array = new Boolean[5];
Arrays.fill(array, Boolean.FALSE);
I'm just curious why the for loop approach doesn't work?
Boolean[] array = new Boolean[5];
for(Boolean value : array) {
value = false;
}
The java enhanced for loop uses an iterator to go through the array. The iterator returns a reference to the object, but java passes the reference by value, so you are unable to change what the reference points to, which is what you are trying to do with value = false.
EDIT:As it turns out, for a normal array, instead of converting to a List and using an iterator, java does the following:
for (int i = 0; i < array.length; i++)
{
Boolean value = array[i]; //here's how we get the value that's referred to
... //in the enchanced for loop
}
While we are not using an iterator, the fact that Java passes references by value still explains what's going on here.
END of EDIT
If this were an array of objects with certain instance members, you would be able change said members, but not what the object, itself, references.
As others have suggested, to get around this, simple use a regular for loop and manually assign values to indexed slots in the array, ie:
Boolean[] b_values = new Boolean[5];
for(int i = 0; i < b_values.length; i++)
{
b_values[i] = Boolean.FALSE;
}
The reason why the code wasn't running is because when you mentioned that
Boolean[] array = new Boolean[5];
for(Boolean value : array) {
value = false;
}
You are actually creating a new reference type called "Array of Boolean" and it contains only references to the five objects of Boolean class but the object doesn't exists as you haven't created them.
While in the second code
Boolean[] array = new Boolean[5];
Arrays.fill(array, Boolean.FALSE);
You are using cached object of Boolean class and adding it to the array you created using java.util.Arrays class. Boolean is a wrapper class in java and as only two possible values can be possible either true and false to avoid the overhead in creating them java already creates them for you and make them available for ready use.
value = true; is internally value = new Boolean(true); i.e it is creating a new Object in the pool. The value object refers to that Boolean object.Wrapper classes are immutable.
Yes Boolean[] can be initialize at for loop. For that you need to set value with array index, instead of enhanced for loop. Have a look at following loop.
Boolean[] array = new Boolean[5];
for(int i=0;i<array.length;i++) {
array[i] = Boolean.FALSE;
}
Because value is a copy of an array element and not the actual element in array.
Boolean[] array = new Boolean[5];
for (int i = 0; i < array.length; i++) {
array[i]= false;
}
Just for reference: How for each works
It's an array of Boolean references with no real object assigned. You need to do this.
Boolean[] array = new Boolean[5];
for(Boolean value : array) {
value = new Boolean(false);
}
EDIT:
This doesn't solve the problem. It's the same. In the for loop, the variable value is not a reference to the original array. That's why you need to do.
Boolean[] array = new Boolean[5];
for (int i = 0; i < array.length; i++) {
array[i]= false;
}
To illustrate Changing the Reference and Changing the value of member if Reference, I tried with User defined class , posting here what I have tried and the observation -thanks #SteveP
//Case 1 Trying to Change Reference
Boolean[] array1 = new Boolean[5];
Arrays.fill(array1, Boolean.FALSE);
for(Boolean value : array1) { // Warning here The value of the local variable value is not used
value = Boolean.TRUE;
}
System.out.println(" Elements ==> "+array1[0]+" - "+array1[1]);
this will print Elements ==> false - false , Reference will not be able to modify
Case 2 Trying to Change Reference with user defined class
MyBool[] array3 = new MyBool[5];
MyBool boolInst2=new MyBool( Boolean.FALSE);
MyBool boolNew=new MyBool( Boolean.TRUE);
Arrays.fill(array3,boolInst2 );
for(MyBool value : array3) { // Warning here The value of the local variable value is not used
value = boolNew;
}
System.out.println(" Elements ==> "+array3[0].flag+" - "+array3[1].flag);
this will print Elements ==> false - false, Reference will not be able to modify
Case 3 Changing the values of members of an object (MyBool.value),
MyBool[] array2 = new MyBool[5];
MyBool boolInst=new MyBool( Boolean.FALSE);
Arrays.fill(array2,boolInst );
for(MyBool value : array2) {
value.flag = Boolean.TRUE;
}
System.out.println(" Elements ==> "+array2[0].flag+" - "+array2[2].flag);
this will print Elements ==> true - true , Values are updated
class MyBool{
public Boolean flag;
public MyBool(Boolean flag){
this.flag=flag;
}
}
In Short:
While Enumerating the array using for loop, you cannot modify the element of collection you are iterating through. Consider it as read only.
This question already has answers here:
Why does the foreach statement not change the element value?
(6 answers)
Closed 5 years ago.
Ok, so I'm tyring to iterate through an ArrayList and remove a specefic element. However, I am having some trouble using the For-Each like structure. When I run the following code:
ArrayList<String> arr = new ArrayList<String>();
//... fill with some values (doesn't really matter)
for(String t : arr)
{
t = " some other value "; //hoping this would change the actual array
}
for(String t : arr)
{
System.out.println(t); //however, I still get the same array here
}
My question in, how can I make 't' a pointer to 'arr' so that I am able to change the values in a for-each loop? I know I could loop through the ArrayList using a different structure, but this one looks so clean and readable, it would just be nice to be able to make 't' a pointer.
All comments are appreciated! Even if you say I should just suck it up and use a different construct.
I think the best approach may be to use a for loop.
ArrayList<String> arr = new ArrayList<String>();
for (int i = 0; i < arr.size(); i++) {
String t = arr.get(i);
if (// your condition is met) {
arr.set(i, "your new value");
}
}
The problem is that you're trying to change the loop-scoped reference t to let it point to a new String instance. This ain't going to work. It does not refer the actual entry in the arraylist. You need to change the actual value of the reference. If String was mutable and provided a fictive set() method for that, you could in theory do
for (String t : arr) {
t.set("some other value");
}
or so, but that's not possible as it is immutable. Better get a handle of the entrypoint in the array itself using the normal for loop:
for (int i = 0; i < arr.size(); i++) {
arr.set(i, "some other value");
}
If you insist in using the enhanced for loop, then you need to replace String by StringBuilder, which is mutable:
for (StringBuilder t : arr) {
t.delete(0, t.length()).append("some other value");
}
Remember, Java is pass-by-value, not pass-by-reference.
For-each doesn't give you an index pointer, so you just can't use it to change an immutable value.
Either use a for-loop with an index or use a mutable type (like StringBuffer, not String)
An array of objects (like strings) in Java is a contiguous block containing an ordered series of references. So, when you have an array of 4 strings, what you really have is 4 references stored IN the array, and 4 string objects that are outside of the array but are referenced by its 4 elements.
What the for-each construct in Java does is create a local variable and, for each iteration, copy into that local variable the reference from the array cell that corresponds to that iteration. When you set the loop variable (t = " some other value") you are putting a reference to a new string, "some other value", into the local variable t, not into the array.
The contrasts with some other languages (like Perl) where the loop variable acts like an alias to the array/list element itself.
Your code is re-written by the compiler as something like this:
ArrayList<String> arr = new ArrayList<String>();
//... fill with some values (doesn't really matter)
for (final Iterator <String> i = arr.iterator(); i.hasNext();) {
String t;
t = i.next();
t = " some other value "; // just changes where t is pointing
}
To do what you want you would have to write the for loop like this:
for (final ListIterator<String> i = arr.iterator(); i.hasNext();) {
final String t;
t = i.next();
i.set("some other value");
}
Iterator does not have the set method, only ListIterator does.
Basically you want to remove the String t from the list arr. Just do a arr.remove(t) and you could be done. But you can't do it while iterating over the same list. You'll get an Exception if you try to modify the list this way.
You have two options:
clone your list, iterate through the clone and remove the 'specific' String from the original list
create a list for delete candidates, add all 'specific' Strings to that list and, after iterating through the original list, iterate through the wastebin and remove everything you've collected here from the original list.
Option 1 is the easist, the clone can be made like:
List<String> clone = new ArrayList<String>(arr);
You seem to misunderstand how objects/references work in Java, which is pretty fundamental to using the language effectively. However, this code here should do what you want (apologies for the lack of explanation):
ArrayList<String> arr = new ArrayList<String>();
//... fill with some values (doesn't really matter)
for(int i = 0; i < arr.size(); i++)
{
arr.set(i, " some other value "); // change the contents of the array
}
for(String t : arr)
{
System.out.println(t);
}
I believe, this is not related to immutable or mutable.
t = " some other value "; //hoping this would change the actual array
t does not hold the reference to actual object. Java copies the value from arraylist and puts that value into t so array list value does not get affect.
HTH
This has been answered well. Still here is my suggestion. The var t inside loop is only visible there. It will not be seen outside the loop. You could do t.set() if it was not String.
Use a StringBuffer rather than plain strings. This way the string within is mutable.
Strings are immutable. If you had a mutable type like StringBuilder/Buffer, you could change the string in your iteration. You do have references, remember.