for :each style question

R

Roedy Green

In a for:each loop, sometimes you want to treat the first and or last
element specially.

The obvious way to handle is to revert to a for int i= loop and check
for special values of i.

You can keep the for:each style if you have a boolean first= true that
you set false to detect the first.

I don't know of an equivalent way to detect the last.

In the olden days I would have handled the first and last cases
outside the loop, with the loop running over the middle elements. You
can't do that with for:each.

What do you consider the best style to deal with this?
--
Roedy Green Canadian Mind Products
http://mindprod.com
For me, the appeal of computer programming is that
even though I am quite a klutz,
I can still produce something, in a sense
perfect, because the computer gives me as many
chances as I please to get it right.
 
A

Arne Vajhøj

In a for:each loop, sometimes you want to treat the first and or last
element specially.

The obvious way to handle is to revert to a for int i= loop and check
for special values of i.

You can keep the for:each style if you have a boolean first= true that
you set false to detect the first.

I don't know of an equivalent way to detect the last.

In the olden days I would have handled the first and last cases
outside the loop, with the loop running over the middle elements. You
can't do that with for:each.

What do you consider the best style to deal with this?

first
for first+1 to last-1
last

Not much point in for(each) when it in fact is not for each.

Arne
 
K

Knute Johnson

In a for:each loop, sometimes you want to treat the first and or last
element specially.

The obvious way to handle is to revert to a for int i= loop and check
for special values of i.

You can keep the for:each style if you have a boolean first= true that
you set false to detect the first.

I don't know of an equivalent way to detect the last.

In the olden days I would have handled the first and last cases
outside the loop, with the loop running over the middle elements. You
can't do that with for:each.

What do you consider the best style to deal with this?

public class test {
public static void main(String[] args) {
int ns[] = new int[23];
int last = 0;

for (int i=0; i<ns.length; i++)
ns = i;

for (int n : ns) {
last = n;
}

System.out.println(last);
}
}

It's not elegant but it works.
 
D

Daniel Pitts

In a for:each loop, sometimes you want to treat the first and or last
element specially.

The obvious way to handle is to revert to a for int i= loop and check
for special values of i.

You can keep the for:each style if you have a boolean first= true that
you set false to detect the first.

I don't know of an equivalent way to detect the last.

In the olden days I would have handled the first and last cases
outside the loop, with the loop running over the middle elements. You
can't do that with for:each.

What do you consider the best style to deal with this?

public class test {
public static void main(String[] args) {
int ns[] = new int[23];
int last = 0;

for (int i=0; i<ns.length; i++)
ns = i;

for (int n : ns) {
last = n;
}

System.out.println(last);
}
}

It's not elegant but it works.


for:each is not useful for detecting last, and any acrobatics you go
through to fix it are fairly pointless.

You would also have to handle the case where first=last (and/or is empty).

I find specific use-cases call for different approaches. For instance,
I like the following for string concatenation:

final StringBuilder builder = new StringBuilder();

String sep = "";

for (Object o: objects) {
builder.append(sep).append(o);
sep = ", ";
}
 
E

Eric Sosman

In a for:each loop, sometimes you want to treat the first and or last
element specially.

The obvious way to handle is to revert to a for int i= loop and check
for special values of i.

You can keep the for:each style if you have a boolean first= true that
you set false to detect the first.

I don't know of an equivalent way to detect the last.

In the olden days I would have handled the first and last cases
outside the loop, with the loop running over the middle elements. You
can't do that with for:each.

What do you consider the best style to deal with this?

I once taught a student whose DO loops (this was FORTRAN) always
iterated from 1 through N, never from 2 through N or 1 through N-1.
Paraphrasing into zero-based Java, he'd write something like

for (int i = 0; i < array.length; ++i) {
if (i == 0) continue;
if (i == array.length - 1) continue;
massage(array[i-1], array, array[i+1]);
}

I was unable to break him of this habit, and eventually formed
the opinion that he had learned The One True Way to write a loop,
and Nothing On Earth would persuade him to write it differently.

That's the wrong way to think about a tool.

The for:each form is a convenience, a prepackaged solution to
a common problem. Do not expect it to be the answer for all kinds
of iteration. for:each is Java's feeble imitation of (mapc); don't
push it beyond its built-in limits. There are lots of other looping
forms available; use them when they suit.

(For those who don't know what (mapc) means, see the current
thread "java developers" and give special attention to Patricia
Shanahan's contribution.)
 
L

Lew

Roedy said:
In a for:each loop, sometimes you want to treat the first and or last
element specially.

The obvious way to handle is to revert to a for int i= loop and check
for special values of i.

You can keep the for:each style if you have a boolean first= true that
you set false to detect the first.

I don't know of an equivalent way to detect the last.

In the olden days I would have handled the first and last cases
outside the loop, with the loop running over the middle elements. You
can't do that with for:each.

What do you consider the best style to deal with this?

Regular 'for' loop (or its 'while' or 'do...while' cousins).

For:each is for a particular use case where it helps. For other use cases, you probably shouldn't use it.
 
R

Robert Klemme

In a for:each loop, sometimes you want to treat the first and or last
element specially.

The obvious way to handle is to revert to a for int i= loop and check
for special values of i.

You can keep the for:each style if you have a boolean first= true that
you set false to detect the first.

I don't know of an equivalent way to detect the last.

The trick for detecting the last element is to defer evaluation of each
element by one iteration and handle the last one after the loop.
In the olden days I would have handled the first and last cases
outside the loop, with the loop running over the middle elements. You
can't do that with for:each.

What do you consider the best style to deal with this?

For the fun of it, here is one way to do it - although I find it quite
silly:

package clj;

import java.util.Arrays;

public final class FirstLast {

private static enum Pos {
NONE, FIRST, LATER
}

public static <T> void firstLast(final Iterable<T> items) {
Pos p = Pos.NONE;
T last = null;

for (final T item : items) {
switch (p) {
case NONE:
p = Pos.FIRST;
break;
case FIRST:
System.out.println("First: " + last);
p = Pos.LATER;
break;
case LATER:
System.out.println("Next: " + last);
break;
}

last = item;
}

if (p != Pos.NONE) {
System.out.println("Last: " + last);
}
}

public static void main(String[] args) {
System.out.println("empty");
firstLast(Arrays.asList());
System.out.println("one");
firstLast(Arrays.asList("a"));
System.out.println("two");
firstLast(Arrays.asList("a", "b"));
System.out.println("more");
firstLast(Arrays.asList("a", "b", "c", "d"));
}

}

Kind regards

robert
 
S

Steven Simpson

In a for:each loop, sometimes you want to treat the first and or last
element specially. [...]
What do you consider the best style to deal with this?

If I needed it a lot...

import java.util.Arrays;
import java.util.List;

public class FirstLastLoop {
interface FirstLastBlock<T> {
void invoke(T t, boolean isFirst, boolean isLast);
}

static<T> void forEach(Iterable<? extends T> coll,
FirstLastBlock<? super T> block) {
boolean isFirst = true;
boolean lastHeld = false;
T last = null;
for (T t : coll) {
if (lastHeld) {
block.invoke(last, isFirst, false);
isFirst = false;
}

last = t;
lastHeld = true;
}
if (lastHeld)
block.invoke(last, isFirst, true);
}

public static void main(String[] args) throws Exception {
List<String> list = Arrays.asList(args);

FirstLastBlock<String> block = new FirstLastBlock<String>() {
public void invoke(String t, boolean isFirst, boolean isLast) {
if (isFirst)
System.out.print("The list: ");
else if (isLast)
System.out.print(" and ");
else
System.out.print(", ");
System.out.print(t);
if (isLast)
System.out.println('.');
}
};

forEach(list, block);

// Lambda version
forEach(list, (t, isFirst, isLast) -> {
if (isFirst)
System.out.print("The list: ");
else if (isLast)
System.out.print(" and ");
else
System.out.print(", ");
System.out.print(t);
if (isLast)
System.out.println('.');
});
}
}
 
C

Chris Riesbeck

first
for first+1 to last-1
last

Not much point in for(each) when it in fact is not for each.

While it's not a Java for:each loop, it's well within the concept of for
each loops in general. The JSTL foreach element has some optional syntax
for accessing just this kind of data :

<c:forEach var="person" items="${group.members}" varStatus="status">
...
</c:forEach>

Inside the forEach, status can tell you if you're first, last, etc.

http://www.ibm.com/developerworks/java/library/j-jstl0318/

There are a couple of things I wish Java's for:each did. The other
biggie is looping across several collections in parallel.
 
D

Daniel Pitts

I always preferred to do it this way:

final StringBuilder builder = new StringBuilder();
final String sep = ", ";
for (final Object o: objects) {
builder.append(o).append(sep);
}
builder.setLength(Math.max(0,builder.length()-sep.length()));

The only drawback is that the size of the builder's internal buffer may
grow unnecessarily, if the last separator crosses its current bounds,
but how often does that happen and how often is it really a problem?
The *only* drawback? Having to invoke a "Math" command is a drawback
IMO. I like my approach because you can have a different starting
separator. Consider the case where you want parenthesis for one or
more items, but an empty string for no items.:

String ender = "";
String sep = "(";
for (Object o: objects) {
builder.append(sep).append(o);
sep=", "; end=")";
}
builder.append(end);

Builds a parenthesized comma-separated list. I have used this pattern
frequently for SQL or HQL query builders.
 
R

Roedy Green

String ender = "";
String sep = "(";
for (Object o: objects) {
builder.append(sep).append(o);
sep=", "; end=")";
}
builder.append(end);

I think that should read
String end = "";
--
Roedy Green Canadian Mind Products
http://mindprod.com
For me, the appeal of computer programming is that
even though I am quite a klutz,
I can still produce something, in a sense
perfect, because the computer gives me as many
chances as I please to get it right.
 
D

Daniel Pitts

I think that should read
String end = "";
indeed, "ender" should be "end" (or vice versa). I've become too
reliant on IDE's auto-suggesting the correct thing...
 
J

Jim Janney

Wanja Gayk said:
I always preferred to do it this way:

final StringBuilder builder = new StringBuilder();
final String sep = ", ";
for (final Object o: objects) {
builder.append(o).append(sep);
}
builder.setLength(Math.max(0,builder.length()-sep.length()));

The only drawback is that the size of the builder's internal buffer may
grow unnecessarily, if the last separator crosses its current bounds,
but how often does that happen and how often is it really a problem?

import org.apache.commons.lang.StringUtils;

....

String joinedObjects = StringUtils.join(objects, ", ");

Not to go all Lew on you here, but the Apache Commons stuff is worth a
second look.
 
L

Lew

Jim said:
import org.apache.commons.lang.StringUtils;

...

String joinedObjects = StringUtils.join(objects, ", ");

Not to go all Lew on you here,

You flatter me, sir.
but the Apache Commons stuff is worth a second look.

.... and a third, and a fourth, ...

That said, there are things in Commons that I would not, or not any longer, use, necessarily. You don't want to use a hammer to unscrew a rivet. But it is good to have a hammer in your toolkit when you need to pound in a nail.
 
D

Daniel Pitts

You flatter me, sir.
Oh, I use StringUtils (and other commons lang classes) frequently. That
wasn't the point.
.... and a third, and a fourth, ...

That said, there are things in Commons that I would not, or not any longer, use, necessarily. You don't want to use a hammer to unscrew a rivet. But it is good to have a hammer in your toolkit when you need to pound in a nail.

I haven't followed the latest versions of commons-lang in a while...
Have they gotten their collections support (lazy maps, etc...) to be
generic yet?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads

two new JDKs 7
Style Tag Problem 1
System.exit 11
Thread question 14
Deployjava.js 2
ant and -bootclasspath 5
Custom fonts 11
Fast String search algorithm 10

Members online

Forum statistics

Threads
473,764
Messages
2,569,566
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top