for :each style question

Discussion in 'Java' started by Roedy Green, Nov 30, 2011.

  1. Roedy Green

    Roedy Green Guest

    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.
     
    Roedy Green, Nov 30, 2011
    #1
    1. Advertising

  2. Roedy Green

    Arne Vajhøj Guest

    On 11/30/2011 6:32 PM, Roedy Green wrote:
    > 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
     
    Arne Vajhøj, Dec 1, 2011
    #2
    1. Advertising

  3. On 11/30/2011 3:32 PM, Roedy Green wrote:
    > 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.

    --

    Knute Johnson
     
    Knute Johnson, Dec 1, 2011
    #3
  4. Roedy Green

    Daniel Pitts Guest

    On 11/30/11 4:49 PM, Knute Johnson wrote:
    > On 11/30/2011 3:32 PM, Roedy Green wrote:
    >> 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 = ", ";
    }
     
    Daniel Pitts, Dec 1, 2011
    #4
  5. Roedy Green

    Eric Sosman Guest

    On 11/30/2011 6:32 PM, Roedy Green wrote:
    > 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.)

    --
    Eric Sosman
    d
     
    Eric Sosman, Dec 1, 2011
    #5
  6. Roedy Green

    Lew Guest

    Roedy Green wrote:
    > 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.

    --
    Lew
     
    Lew, Dec 1, 2011
    #6
  7. On 12/01/2011 12:32 AM, Roedy Green wrote:
    > 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
     
    Robert Klemme, Dec 1, 2011
    #7
  8. On 30/11/11 23:32, Roedy Green wrote:
    > 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('.');
    });
    }
    }



    --
    ss at comp dot lancs dot ac dot uk
     
    Steven Simpson, Dec 4, 2011
    #8
  9. On 11/30/2011 6:28 PM, Arne Vajhøj wrote:
    > On 11/30/2011 6:32 PM, Roedy Green wrote:
    >> 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.
    >


    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.
     
    Chris Riesbeck, Dec 5, 2011
    #9
  10. Roedy Green

    Daniel Pitts Guest

    On 12/10/11 4:28 AM, Wanja Gayk wrote:
    > In article<iyBBq.2059$>,
    > says...
    >
    >> 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 = ", ";
    >> }

    >
    > 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.
     
    Daniel Pitts, Dec 12, 2011
    #10
  11. Roedy Green

    Roedy Green Guest

    On Mon, 12 Dec 2011 12:07:25 -0800, Daniel Pitts
    <> wrote, quoted or indirectly
    quoted someone who said :

    >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.
     
    Roedy Green, Dec 13, 2011
    #11
  12. Roedy Green

    Daniel Pitts Guest

    On 12/13/11 8:08 AM, Roedy Green wrote:
    > On Mon, 12 Dec 2011 12:07:25 -0800, Daniel Pitts
    > <> wrote, quoted or indirectly
    > quoted someone who said :
    >
    >> 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 = "";

    indeed, "ender" should be "end" (or vice versa). I've become too
    reliant on IDE's auto-suggesting the correct thing...
     
    Daniel Pitts, Dec 13, 2011
    #12
  13. Roedy Green

    Jim Janney Guest

    Wanja Gayk <> writes:

    > In article <iyBBq.2059$>,
    > says...
    >
    >> 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 = ", ";
    >> }

    >
    > 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.

    --
    Jim Janney
     
    Jim Janney, Dec 14, 2011
    #13
  14. Roedy Green

    Lew Guest

    Jim Janney wrote:
    > Wanja Gayk writes:
    >> says...
    >>
    >>> 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 = ", ";
    >>> }

    >>
    >> 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,


    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.

    --
    Lew
     
    Lew, Dec 15, 2011
    #14
  15. Roedy Green

    Daniel Pitts Guest

    On 12/14/11 4:53 PM, Lew wrote:
    > Jim Janney wrote:
    >> Wanja Gayk writes:
    >>> says...
    >>>
    >>>> 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 = ", ";
    >>>> }
    >>>
    >>> 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,

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

    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?
     
    Daniel Pitts, Dec 16, 2011
    #15
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. John Blair
    Replies:
    1
    Views:
    411
    Eliyahu Goldin
    Aug 3, 2005
  2. Tjerk Wolterink

    xsl:for-each for each 3 elements problem

    Tjerk Wolterink, Nov 3, 2004, in forum: XML
    Replies:
    3
    Views:
    429
    Tjerk Wolterink
    Nov 3, 2004
  3. Ken Varn
    Replies:
    0
    Views:
    472
    Ken Varn
    Apr 26, 2004
  4. Pat Maddox
    Replies:
    6
    Views:
    158
    Marcin Mielżyński
    Jan 20, 2006
  5. Igor Nn
    Replies:
    7
    Views:
    437
    Johnny Morrice
    May 28, 2011
Loading...

Share This Page