Refactor foreach to for loop - java

I have a foreach loop in Java (simplified version here)
List<String> names = getNames();
for(String name:names) {
doSomething(name);
}
Is there an automated way to refactor this to a traditional for loop?
I know how to do it manually
List<String> names = getNames();
for(int i=0; i<names.size(); i++) {
String name = names.get(i);
doSomething(name);
}
As you can see, there is a bit of typing needed in the for statement itself as well as introducing the variable name again and assign it the value names.get(i). In total, the manual edit is too error-prone for me.
Why do I want to do this? I have to fix a bug and the fix is to start at index 1 instead of index 0 and end at index n-1 instead of the end (unfortunately I can't fix the input right away, I need to wait for a library update if that's recognized as a bug).
What have I tried? I right-clicked on the for keyword and clicked on "Refactor", but as far as I can get from the context menu entries, nothing in there would do the work for me.
Why do I think this could theoretically work? Because similar functionality exists in Resharper for Visual Studio (C#).
FYI: I'm using Eclipse Luna SR 2 (4.4.2)

Mouseover the for statement, right-click, Quick fix (Ctrl+1), convert to indexed loop.
Should work!

List<String> names = getNames();
names = names.subList(1, names.size() - 1);
for(String name : names) {
doSomething(name);
}
Of course, you could put that into a reusable method if you need to do it several times:
public static List<String> fixList(List<String> names) {
return names.subList(1, names.size() - 1);
}
and then use it as
List<String> names = fixList(getNames());
for(String name : names) {
doSomething(name);
}

In my eclipse (Kepler RC2) it works to select the for keyword and either use the quick fix from the context menu or hit CTRL+1 for the shortcut. Eclipse then offers me "Convert to indexed 'for' loop" or "Convert to Iterator-based 'for' loop".

You can use either:
names = names.subList(1, names.size()-1);
for (String name : names) {
doSomething(name);
}
or in manually:
for (int i = 1; i < names.size()-1; i++) {
String name = names.get(i);
doSomething(name);
}
But the first one I prefer to use.

Be careful with this refactoring.
The reason is that for a traditional linked list, your second for loop formulation is O(N * N) since you're having to traverse the linked list in order to evaluate names.get(i);. That could become expensive.
Do consider performance implications when moving from for(String name:names) {. There may well be a better way of fixing your immediate bug, and retaining the current "Big O".
for(String name : names.subList(1, names.size() - 1)) {
is one such way (Acknowledge #JB Nizet).

Don't go for indexed iteration!
This does not perform well with all implementations of List.
It is much better to go for an Iterator and let the JIT optimize the Iterator away if this code is hot.
So either write this:
List<String> names = getNames();
for (String name : names.subList(1, names.size() - 1)) {
doSomething(name);
}
Or that (one allocation less):
Iterator<String> it = getNames().iterator();
it.next(); // You seem to be sure there is more than one element in the list
while (it.hasNext()) {
String name = it.next();
doSomething(name);
}

In my case, I need to transform the foreach to use an index .
This is my two cents
Integer index=0;
for (final String OneCell :CellList){
// Your code use the index
index=index+1;
}

When using java 8 you could use the stream api
names.stream().skip(1).reverse().skip(1).reverse().foreach(
name -> do something(name)
);
Something like this...

Related

detect last foreach loop iteration

Supposing that I have some foreach loop like this:
Set<String> names = new HashSet<>();
//some code
for (String name: names) {
//some code
}
Is there a way to check inside foreach that the actual name is the last one in Set without a counter? I didn't found here some question like this.
For simplicity and understandability, imo, would do:
Set<String> names = new HashSet<>();
Iterator<String> iterator = names.iterator();
while (iterator.hasNext()) {
String name = iterator.next();
//Do stuff
if (!iterator.hasNext()) {
//last name
}
}
Also, it depends on what you're trying to achieve. Let's say you are implementing the common use case of separating each name by coma, but not add an empty coma at the end:
Set<String> names = new HashSet<>();
names.add("Joao");
names.add("Pereira");
//if the result should be Joao, Pereira then something like this may work
String result = names.stream().collect(Collectors.joining(", "));
Other answears are completely adequate, just adding this solution for the given question.
Set<String> names = new HashSet<>();
//some code
int i = 0;
for (String name: names) {
if(i++ == names.size() - 1){
// Last iteration
}
//some code
}
There isn't, take a look at How does the Java 'for each' loop work?
You must change your loop to use an iterator explicitly or an int counter.
If you are working with a complex object and not just a plain list/set the below code might help. Just adding a map function to actually get the desired string before you collect.
String result = violations.stream().map(e->e.getMessage()).collect(Collectors.joining(", "));
Yes, there is a way to check it inside of foreach, by use of a counter:
Set<String> names = new HashSet<>();
int i = names.size() - 1;
for (String name: names) {
if (i-- == 0) {
// some code for last name
}
//some code
}
Consider, names.size() is called only one time outside of the loop. This makes the loop faster than processing it multiple times within the loop.
There is no build in method to check if the current element is also the last element. Besides that you are using a HashSet which does not guarantee the return order. Even if you want to check it e.g. with an index i the last element could always be a different one.
A Set does not guaranty order over of items within it. You may loop through the Set once and retrieve "abc" as the "last item" and the next time you may find that "hij" is the "last item" in the Set.
That being said, if you are not concerned about order and you just want to know what the arbitrary "last item" is when observing the Set at that current moment, there is not built in way to do this. You would have to make use of a counter.
.map(String::toString) from the answer above is redundant, because HashSet already contains String values. Do not use Set to concatenate strings because the order is not assured.
List<String> nameList = Arrays.asList("Michael", "Kate", "Tom");
String result = nameList.stream().collect(Collectors.joining(", "));
There is an easy way you can do this throw one condition.
consider this is your array:
int odd[] = {1,3,5,7,9,11};
and you want to print it all in one line with " - " hyphen between them except the last one.
for(int aa:odd) {
System.out.print(aa);
if(odd[odd.length - 1] != aa)
System.out.print(" - ");
}
this condition
if( odd[odd.length - 1] != aa )
will check if you aren't in the last element so you can still add " - ", otherwise you will not add it.
List<String> list = Arrays.asList("1", "2", "3");
for (String each : list) {
if (list.indexOf(each) == (list.size() - 1)) {
System.out.println("last loop");
}
}
Note: Set is NOT an ordered collection.

Java for each statement

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.

How to convert Vector elements to comma separated String

I am having a Vector<String> containing some items. I want to search those items in database.
For that purpose I need to make a String consisting of comma separated items out of that Vector so that I can pass it to SQL Query.
I am doing something like this.
StringBuilder list = new StringBuilder("");
for (String items : itemList) {
if(its last element then) //How to check here
list.append(items);
else
list.append(items+",");
}
but that will return me the output like "item1,item2,item3,"
where as I want to omit the last comma (,) in case if it is last element of Vector.
I have checked Vector API and found this method lastElement() (which returns last element not boolean).
How can I check it in the loop or is there any smarter/efficient way to do it?
I would go with the String.join approach.
final List<String> itemList = Arrays.asList("item1", "item2", "item3");
final String commaSeparated = String.join(",", itemList);
System.out.println(commaSeparated); // -> item1,item2,item3
A nice and clean solution. Only available for Java 8.
This is a possible duplicate of Java: function for arrays like PHP's join()?
Don't reinvent the wheel! This has been done many many times, unit tested and maintained. so use them, don't make your own.
You can use Apache Commons StringUtils and use the join method on Iterable (which Vector is)
StringUtils.join(itemList, ", ")
Also, as pointed out by Tom in the comments of your question, in Java 8 you can use String.join()
Have a look at libraries like Google Guava
Joiner.on(",").useForNull("null").join(itemList)
Since you're using a StringBuilder, it's easier to just delete the last comma:
StringBuilder list = new StringBuilder("");
for (String items : itemList) {
list.append(items).append(',');
}
if ( list.length() > 0 ) {
list.setLength(list.length() - 1 );
}
Change your loop as below:
for (int i=0; i<itemList.size(); i++) {
list.append(itemList.get(i));
if(i != itemList.size() - 1) {
list.append(",");
}
Internally vector uses array and you are accessing element by index now instead of using advance for each loop.
There are several third party libraries that do the heavy lifting for you, like Apache Commons Lang's StringUtils:
String list = StringUtils.join(items, ',');
If you absolutely must do this yourself, I'd start with the first element and append a comma and an element for each succeeding element:
StringBuilder list = new StringBuilder();
Iterator<String> iter = items.iterator();
if (iter.hasNext()) {
// Special treatment for the first item
list.append(iter.next());
// The rest of the items
while (iter.hasNext()) {
list.append(',').append(iter.next());
}
}
Simply use,
StringUtils.join(itemList, ", ");

For-Each and Pointers in Java [duplicate]

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.

Java: Alternative to iterator.hasNext() if using for-each to loop over a collection

I'm trying to replace an iterator-based loop over a Java list with a for-each statement, but the code uses at some point iterator.hasNext() to check if it reached the last element in the list.
Is there something similar for the for-each alternative?
for (Object current : objectList) {
if (last-element)
do-something-special
}
for-each is just syntactic sugar for iterator version and if you check compiled bytecode, then you'll notice that compilator actually change it into iterator version.
With a for-each form you can't check whether you'll have more elements or not.
Just stay with explicit iterator use if you need that feature.
In addition to Luno's answer:
Iterator<MyClass> it = myCollection.iterator();
while(it.hasNext()) {
MyClass myClass = it.next():
// do something with myClass
}
translates to:
for (MyClass myClass:myCollection) {
// do something with myClass
}
As others have said - this isn't possible.
Just remember that the foreach construct isn't the be-all and end-all. It was introduced to make the very common task of performing the same operations on each element of a collection simpler to denote.
In your case, you don't want to do exactly the same thing to each element - and thus a foreach loop is not the right tool for the job. Trying to "hack" it into doing this is silly, just use an explicit iterator in a classic for loop.
The foreach loop (or enhanced for loop) does not have facilities to keep track of which element is being iterated on at the moment. There is no way to find out which index of a Collection is being worked on, or whether there are more elements to be processed in an Iterable.
That said, one workaround that would work is to keep a reference to the object which is being iterated on at the moment in the foreach loop.
By keeping a reference of what it being worked on at the current iteration, one would be able to keep the reference once the foreach loop ends, and what is left in the variable will be the last element.
This workaround will only work if-and-only-if the last element is the only element which is needed.
For example:
String lastString = null;
for (String s : new String[] {"a", "b", "c"}) {
// Do something.
// Keep the reference to the current object being iterated on.
lastString = s;
}
System.out.println(lastString);
Output:
c
Unfortunately, the for each idiom does not allow you to check if an element is first or last in the list. This is a known limitation of the for each loop.
I suggest you just keep using the iterator.
If you can also check for the first element instead of the last one, for example if you're doing String concatenation, you could change to something like:
boolean first = true;
for (Element e : list) {
if (!first) {
//do optional operation
}
//do other stuff
first = false;
}
but I would prefer using the iterator.
If you want to stay with for-each maybe something like this:
if (objectList.indexOf(current)==objectList.size()-1) break;
int nElts = objectList.size();
int n = 0;
for (...) {
if (++n == nElts) ...
is the best I can think of.
There are two possible cases where you would like to do this.
You need to do something after the last element has been reached: in this case you just need to put your code outside of the loop.
for(Object item:theLinkedList){
}
System.out.println("something special")
you need to modify the last element in some way or use information related to the last element. In this case you should use the **LinkedList** to access the last element
for(Object item:theLinkedList){
}
Object last=theLinkedList.getLast();
System.out.println("last");
yes you can, here's how i would do it if you don't want to use the explicit iterator syntax:
for (Object current : objectList) {
if (objectList.getLast().equals(current))
do-something-special
}
In Addition to bayer you have to do it a bit different because there is no method getLast(). But instead of it you can use this objectList.size() - 1.
for (Object current : objectList) {
if (objectList.get(objectList.size() - 1).equals(current))
do-something-special
}
Just save loop repeat count, sample :
int loop = 0;
for(Item item : items) {
if(loop == 0) {
//Is First Item
}
if(loop != items.size()-1) {
//Has Next Item
}
if(loop == items.size()-1) {
//Is Last Item
}
//Must Be Last Statement
loop++;
}
It's similar to for(int i = 0; i < items.size(); i++) loop;
items.size() is used for lists and items.length for arrays;

Categories

Resources