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.
Related
This question already has answers here:
Removing empty element from Array(Java)
(5 answers)
Closed 6 years ago.
I'm trying to remove a specific character from an array by copying the non specified elements into a new array. However, when I initialize new array, the return value is giving me a bunch of null values instead of the non specified element array.
public String[] wordsWithout(String[] words, String target) {
String store[] = null;
for(int i = 0; i < words.length; i = i +1){
store = new String[words.length];
if(!words[i].equals(target)){
store[i] = words[i];
}
}
return store;
}
You are initializing the result array inside the loop. In other words, for each iteration of the loop, you initialize a new array, and lose the changes you made to the previous one. You should move the initialization outside the loop.
But that would also pose a problem, as you wouldn't be able to preemptively know the size of the resulting array. Java 8 allows you a much easier way to write such a method:
public String[] wordsWithout(String[] words, String target) {
return Arrays.stream(words)
.filter(w -> !w.equals(target))
.toArray(String[]::new);
}
EDIT:
As noted in the comments, the design of the OP could be preserved, and the results could be accumulated in a List. IMHO, it's less elegant, but it would work in older versions of Java too:
public String[] wordsWithout(String[] words, String target) {
List<String> store = new ArrayList<>();
for (String word : words) {
if (!word.equals(target)){
store.add(word);
}
}
return store.toArray(new String(store.size());
}
You are initializing the array each time in for loop. Please check.
This is happening because you have of this line: store = new String[words.length]. This means that you are initializing your new Array to an Array of words.length elements all of them being initialized by default with null.
The other problem I see is that you are initializing your Array inside the loop, so at each iteration you will overwrite the changes you have already made in the previous loop (if made any)
To copy the non-specified elements in your new array I will do like #Mureinik mentioned above.
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.
I'm really good with VB and I have a project where I need to check an array. If the same item in an array exists twice or more it needs to be changed to an item that doesn't exist. Now I'm in a class where they're making us use Java for this project.
I was wondering what is the equivalent of a for each loop in Java? I checked the JavaDocs and it only had info for the regular for loop, I didn't notice any section that said anything about a for each loop.
It's more subtle in Java than VB. You can find the official docs in the Oracle documentation here (towards the bottom):
Java For Loops
The provided example is:
// Returns the sum of the elements of a
int sum(int[] a) {
int result = 0;
for (int i : a)
result += i;
return result;
}
Hope that helps. Be careful not to remove or add elements inside the loop or you will get a Concurrent Modification Exception.
try
String arr [] = // you decide how this gets initialized
for (String obj: arr) {
}
This is called "iterating over collections". An array can be implicitly converted to a collection, so you can iterate over an array in the same way, using the "enhanced for-loop".
List<String> names = new LinkedList<>();
// ... add some names to the collection
for(name:names) {
System.out.println(name);
}
I'm not sure if VB has collections - they are a big part of Java and I recommend you look into them.
Of course this changes a bit in Java 8, although you'll notice a collection is still the backbone of forEach().
List<String> names = new LinkedList<>();
// ... add some names to the collection
names.forEach(name -> System.out.println(name));
A for each loop (also known as the enhanced for loop) is as follows:
for (String name : names) {
// here, the loop will work over each element of 'names',
// with the variable name with which to access each element
// being 'name', and output it
System.out.println(name);
}
A normal for loop is as follows:
for (int i = 0; i < max; i++) {
// here, i will iterate until max, then the loop will stop.
// any array access here has to be done manually using i, which increments.
}
If insertion order from the names array is important, keep adding the objects to a LinkedHashSet<String>, then with either a for loop or enhanced for loop or iterator, go over your list of names and add each of them to the LinkedHashSet. If the add method, passing in your name, returns false, generate a new name and add that.
If insertion order is not important, use a HashSet<String> instead.
At the end, convert back to an array if it is important (String[] bla = map.toArray(new String[0])), or output the toString() of the map.
Hi i am trying to add the values to list as show in below code. i am getting error.
for (String n2 : number ) {
List<String> ARRAY = new ArrayList<String>();
if (!ARRAY.contains(number)) {
ARRAY.add(number);
}}
But getting error while adding the number in to list.
error
java:271: error: no suitable method found for add(List<String>
ARRAY.add(number);
^
method List.add(int,String) is not applicable
(actual and formal argument lists differ in length)
method List.add(String) is not applicable
(actual argument List<String> cannot be converted to String by method invo
if i use like below
for (String n2 : number ) {
List<String> ARRAY = new ArrayList<String>();
if (!ARRAY.contains(n2)) {
Email(n2);
ARRAY.add(n2);
}
}
if i am using above. Though already email sent with value n2 again it is sending again.
For first it has to sent but for second time since it is already in array it should not sent right?
Problem with your code is you are adding number instead of n2
Change the code like this
List<String> ARRAY = new ArrayList<String>();
for (String n2 : number ) {
if (!ARRAY.contains(n2)) {
ARRAY.add(n2);
}
}
1) ARRAY should be outside of your for loop.
2) Replace if (!ARRAY.contains(number)) to if (!ARRAY.contains(n2 )).
Your code need to like this
List<String> ARRAY = new ArrayList<String>();
for (String n2 : number ) {
if (!ARRAY.contains(n2 )) {
ARRAY.add(n2);
}
}
You're trying to add a collection - numbers into a List of Strings.
I am assuming you are trying to add n2 into ARRAY.
ARRAY.add(n2);
I am afraid there is a bit more wrong with your code than just that one error. As has been pointed out many times, you are trying to add an iterable collection of strings, number to your Array rather than n2 which is the iteration variable. If you want to add complete Collection instances you can do so using addAll().
As for the rest, I strongly recommend sticking to the Java naming convention and using lower case names for your variables. This will improve readability as many members of the community stick with that convention. You can find a neat write-up here.
You also seem to, unless your code is highly simplified, make the mistake of declaring an ArrayList inside the scope of a loop. you are instantiating a new ArrayList every time you enter the loop. I am not sure that is what you want to do. Be sure to check your design.
Also, if you simply want to avoid having duplicate values, I would suggest using Set as it performs the check automatically using the hashCode() of each member on insertion to check for collisions. Try doing:
HashSet<String> uniqueSet = new HashSet<>(number);
You should now have a Collection of unique strings.
number is collection of string and you are adding int ARRAY instead add n2
List<String> ARRAY = new ArrayList<String>();
for (String n2 : number ) {
if (!ARRAY.contains(n2)) {
ARRAY.add(n2);
}
}
List<String> ARRAY = new ArrayList<String>();
for (String n2 : number ) {
if (!ARRAY.contains(n2)) {
ARRAY.add(n2);
}
}
List<String> ARRAY = new ArrayList<String>(); //have this out of the thread
I just started using the enhance for-loop. I want to know if I can use this loop to copy an array. I want to iterate through every element of a certain array and copy it to a new one. It would also be nice to use the enhanced for-loop to instantiate my new array (instead of a typical for-loop). In my current implementation I do know how big I want the array to be, but for future reference I would like to know if I can do this, and if so, how.
My specific plans for what I'm doing might help to answer my question. What I am doing is retrieving a line of text from a text file then calling split( "," ) on that string - this returns an array of Strings. I want to store this array in memory so I can play with it later.
The way I understand the enhanced for-loop to work is that the first value is assigned the current position in the array and the second value is the array that is to be traversed.
I was wondering if there are other formats for for-loops, besides: for ( initialization; termination; iterate ) and for ( Object o : list[] ).
If you want to keep to the enhanced for loop for copying an array, there is one mayor problem: the enhanced for loop doesn't have a counter. Inserting elements into an array however requires a counter. So you could of course do this manually like so:
String[] array = {"A", "Bb", "c", "dD"};
String[] newArray = new String[array.length];
int i=0;
for(String stuff : array) {
newArray[i++] = stuff;
}
This is entirely possible but not really the idea behind the enhanced for loop.
More in line with the intention would be something like this:
String[] array = {"A", "Bb", "c", "dD"};
List<String> list = new ArrayList<String>();
for(String stuff : array) {
list.add(stuff);
}
String[] newArray = new String[list.size()];
list.toArray(newArray);
That way not only do you follow the idea behind the enhanced for loop, you also allow for the possibility that array.length() != newArray.length() (because, say, you filtered the array).
EDIT: as of Java 7, there are indeed only the two for loops you mentioned. This may change in future versions though if it seems sensible; after all, the enhanced for loop was only added in Java 5 (as can be seen here).
To my knowledge, there are only standard for(init; termination; iterataion) loops and for-each for(type o : iterable) loops.
First, knowing the size ahead of time shouldn't be a concern. For instantiating the new array use the original array's .length field: new String[original.length]; as shown below.
Moving along, for what you are doing, the standard for loop is appropriate for two reasons:
You would need to nest two for-each loops in order to iterate both
loops, making it more hassle than a standard for loop. (or add an externally defined counter, as in blalasaadri's solution)
More importantly, in the case of a primitive data type or a String, the variable declared before the : in the for-each loop represents the value of each successive element, and is not a reference to the actual element. As such, any changes to the variable are gone once the loop iterates. I'm not sure if this holds for 'normal' objects (ie: not String), as I've not tried, though I want to find out now.
To illustrate:
String[] sArr = {"foobar"};
for(String s : sArr){
s = "openbar";
}
is the equivalent of:
String[] sArr = {"foobar"};
String s = sArr[0];
s = "openbar";
Sadly, for sArr[0], there is no open bar, same old foobar.
As for solutions, if you can import java.util.Arrays; then try:
String[] copyStrings = Arrays.copyOf(arrStrings, arrStrings.length);
Or, if you need to roll your own:
public String[] copyArray(String[] original){
String[] dupe = new String[original.length]; //I assume you want equal length
for(int i = 0; i < original.length; i++){
dupe[i] = original[i]; //single iterator traverses both arrays
}
return dupe;
}
// copy contents of Object[] arr1 into Object[] arr2
arr2 = new int[arr1.length];
int i=0;
for(Object c:arr1){ //store an element of arr1 in c iteratively
arr2[i] = c;
i++;
}