• Post Reply Bookmark Topic Watch Topic
  • New Topic

Yay or Nay: Counter in For-each Loop  RSS feed

 
Kat Rollo
Ranch Hand
Posts: 62
Eclipse IDE Java MySQL Database
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi guys,

I've seen some debate at StackO discouraging counters inside for-each loops. I just want to know what are your thoughts on this?
Sure it works, but is it desirable practice?
 
Tim Cooke
Marshal
Posts: 4051
239
Clojure IntelliJ IDE Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Not sure what you mean by this. Can you elaborate with an example?
 
Kat Rollo
Ranch Hand
Posts: 62
Eclipse IDE Java MySQL Database
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi,

Here is a method that takes a String input and converts it to a char[]. Then it extracts all odd numbers and puts them in an int[]:

Was it okay to use ctr here?
 
Mike. J. Thompson
Bartender
Posts: 689
17
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
It depends what you mean by 'ok'. It will certainly functionally work, but it is using a for-each loop for something it is not intended for. It would be more maintainable to use a normal for-loop in this situation because it naturally provides you with a counting mechanism so people will be expecting to see that.

However if I had to implement this function (and I wasn't using Java 8 so can't use the Streams API) then I would use a for-each loop and put the selected items into a List.

This let's you use a for-each loop without a counter, but if you really need an array at the end you can use List.asArray(...). The big advantage of this is you know how many items are in the array simply by looking at its length, rather than possibly having an array that is too long with empty slots.

Edit: Although when dealing with ints I suspect List.toArray() might struggle to create an int[] but would instead want to produce an Integer[].
 
Mike Simmons
Ranch Hand
Posts: 3090
14
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Seems to make perfect sense here. The counter is not the same as the index for array caUserInput, so it makes sense to use a different variable.

Anyway, more generally: if you actually need a counter, for anything other than accessing elements in the original array or list, then sure, use a counter. But if the only reason you need a counter is to access array or list elements by index, then you might as well just use a foreach loop - that's what it's there for. Most of the time, that's all you need. But if you need a counter for something else, feel free to use one. No big deal.
 
Kat Rollo
Ranch Hand
Posts: 62
Eclipse IDE Java MySQL Database
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Actually, my initial method used a List<Integer>:

Which worked as intended, but I was trying to achieve the same with a regular for-loop and regular int[].
Can anyone kindly show me how? I just want to study the differences.
 
Mike. J. Thompson
Bartender
Posts: 689
17
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
That is true, the counter is not the same. I shouldn't post first thing after waking up!
 
Mike Simmons
Ranch Hand
Posts: 3090
14
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Although in this case, you only seem to need the counter to know the current index in the second array. So like Mike Thompson said, I'd add the numbers to a List (e.g. ArrayList) instead, and just return that. A List will keep track of its own current size for you, and grow as necessary. Also, when you return a List, the calling code will be able to know how many elements there are in the list. You don't have that feature when you return the array oddNumbers here - the calling code will have to test for nonzero values in the array.
 
Mike Simmons
Ranch Hand
Posts: 3090
14
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Mike: well, maybe I shouldn't post just before going to sleep, as I see I'm now making several points you've already made. Well, one way or another we've got it covered now.
 
Kat Rollo
Ranch Hand
Posts: 62
Eclipse IDE Java MySQL Database
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi guys,

Thanks for your responses. It seems our posts overlapped.
Kindly check this post.
Is that a more desirable approach?
 
Winston Gutkowski
Bartender
Posts: 10575
66
Eclipse IDE Hibernate Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Kat Rollo wrote:Was it okay to use ctr here?

Sure, but let's look at another way of doing it:Which do you prefer? I have a slight preference for the one above because the loop is self-contained (ie, all its control variables are part of the loop itself). I suspect it might be a bit quicker too, but that's a very bad reason to choose one style over another.

The fact is that there's no "right" answer to your question. For-each loops are nice when you want to process items in an array or an Iterable (and that includes all Java Collections), but they don't allow you to declare local variables (maybe that'll be added in a future release - I hope so).

They also usually perform consistently. If userInput was a LinkedList, the above loop (if it used get(i)) could be horribly slow, whereas a for-each loop would be uniformly fast.

So: you pays yer money and you takes yer choice.

Winston

 
Campbell Ritchie
Marshal
Posts: 56599
172
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Kat Rollo wrote: . . . Then it extracts all odd numbers and puts them in an int[]:
. . .
Challenge to you: find out why that technique will find
all odd numbers
for chars and will not find
all odd numbers
in an array of ints.

Also tell us why your array will probably not be full of odd numbers when you finish.
 
Piet Souris
Master Rancher
Posts: 2044
75
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
@Winston
I prefer Kats way. Your method might suffer from the fact that 'ctr' is now only local
to the for-loop, and therefore not known right after the loop. And although the
current sloution does nothing with the 'ctr', I think it should be used to indicate
the 'length' of the array that is returned.

For this reason, like others have mentioned, I would prefer to use a List.

Greetz,
Piet
 
Winston Gutkowski
Bartender
Posts: 10575
66
Eclipse IDE Hibernate Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Piet Souris wrote:For this reason, like others have mentioned, I would prefer to use a List.

I absolutely agree. I was simply showing an alternative to for-each. Hopefully, one of these days, Java will add support for local variables to it, eg:

for(int i = 0; char ch : userInput.toCharArray(); i++) { ...

rather than forcing us to declare them externally.

Winston
 
Stephan van Hulst
Saloon Keeper
Posts: 7993
143
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
That doesn't make sense to me, Winston.

I don't like cluttering the for statement with variables that don't actually control the loop. If you need the result of i outside of the loop, declare it outside the loop. If you only need i inside the loop, declare it inside. Declaring it in the for statement only serves to confuse, in my opinion.

Regardless, I pose a scenario that creates yet another interesting for-loop. Let's say you have a list of numbers, and you have to *remove* odd numbers, and return those in a new list:
 
Winston Gutkowski
Bartender
Posts: 10575
66
Eclipse IDE Hibernate Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Stephan van Hulst wrote:That doesn't make sense to me, Winston.
I don't like cluttering the for statement with variables that don't actually control the loop. If you need the result of i outside of the loop, declare it outside the loop. If you only need i inside the loop, declare it inside. Declaring it in the for statement only serves to confuse, in my opinion.

Hmmm, well maybe we'll have to agree to disagree there. I (and I'm pretty sure the designers) have never assumed that the "initialization" part of a for loop was restricted solely to control variables; although that was maybe the primary role.

How often have you seen things like:
for (int i = 0, e = string.length(); i < e; i++) { ...
as an efficiency suggestion? I use it all the time.

Obviously, if you need the value outside the loop, you should declare it outside; but what if you only need it as an index inside, or as a counter to know that you're on the "nth" iteration? The whole point of the for-each loop was to save us from having to deal with Iterators and all their clunky logic.

Winston
 
Stephan van Hulst
Saloon Keeper
Posts: 7993
143
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I would argue that in your example, e is a control variable, so it has a place in the for statement. Counters are not, they should be declared either in or out.

But sure, I can agree to disagree
 
Winston Gutkowski
Bartender
Posts: 10575
66
Eclipse IDE Hibernate Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Stephan van Hulst wrote:But sure, I can agree to disagree

Hey, that's what makes programming fun. It'd be a sorry excuse for a profession if we all did things the same way.

Winston
 
Campbell Ritchie
Marshal
Posts: 56599
172
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
There is a difference between using e in the loop and the conventional way to write a loop. In the example here we found that the length of the array is reassessed every iteration. So using e gives a slight performance enhancement. It works as long as you can be sure the value of e does not change.

And I knew there was something not quite right about this thread:

It should be spelt yea not yay
 
Winston Gutkowski
Bartender
Posts: 10575
66
Eclipse IDE Hibernate Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Campbell Ritchie wrote:So using e gives a slight performance enhancement...

Actually, my point was more about being able to define local variables. The for loop gives you a nice compact way to do this and also to update them at the end; and while I'm happy to concede Stephan's point that it was probably intended for control variables, I think that actually being able to define any local variable (within reason) is one of for's greatest strengths.

So, why not just allow the central part of a for loop to be either a condition OR an "iterator" expression (signified by the ":").?

Don't worry, I'm not going to hold my breath about it; but I reckon it would be a nice option.

Winston
 
Kat Rollo
Ranch Hand
Posts: 62
Eclipse IDE Java MySQL Database
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Campbell Ritchie wrote:It should be spelt yea not yay

Haha yeah, it's just a play around how fashion TV shows do it "Yay or Nay". ;)

Honestly, I don't see

very often. At first, I actually thought it was a typo. ^_^;

But thanks for your responses guys. I can now pretty much say putting a ctr inside a foreach isn't that much of a crime if it's not being solely used to simply traverse the same array.
So pretty much the main concern of the thread has been addressed.

I did use a List<Integer> before I tried int[], but my prof said it can be expensive, so I was trying if I can do with regular int[].
I was able to do so, but my problem was 0's filled in the rest of the int[].
 
Mike. J. Thompson
Bartender
Posts: 689
17
  • Likes 2
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
To answer your professors point about List<Integer> being more expensive than int[]: maintainability and especially readability are more important than performance until you have firm evidence that your application is not as performant as it is required to be.

If we start complaining that one thing is more expensive than another just for the sake of it then we would end up writing everything in assembler.

And I do believe it would be possible to rewrite your method as a pure for-loop without needing to increment your counter in the for loop body. It would probably not be as readable as the other solutions though.
 
Kat Rollo
Ranch Hand
Posts: 62
Eclipse IDE Java MySQL Database
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Mike. J. Thompson wrote:And I do believe it would be possible to rewrite your method as a pure for-loop without needing to increment your counter in the for loop body. It would probably not be as readable as the other solutions though.

Hi Mike,

I'm interested to see this. Would you kindly show how?
 
Winston Gutkowski
Bartender
Posts: 10575
66
Eclipse IDE Hibernate Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Mike. J. Thompson wrote:To answer your professors point about List<Integer> being more expensive than int[]: maintainability and especially readability are more important than performance until you have firm evidence that your application is not as performant as it is required to be.

@Kat: Which is a long way of saying "premature optimization is the root of all evil".

And, to be honest, I'm not even sure about the "premature" part of that well-known chestnut. In my experience, almost all efforts at "optimization" have resulted in either the exact opposite, or some really obscure bug that manifests itself about six months after all your "optimizers" have left town (see Sod's Law).

I much prefer W.A. Wulf's quote:
"More computing sins are committed in the name of efficiency (without necessarily achieving it) than for any other single reason — including blind stupidity."

Winston
 
Mike. J. Thompson
Bartender
Posts: 689
17
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
@Kat

Ok, but with the caveat that I'm not advocating this is a good way to do it. Also I don't gave access to a compiler right now, and its amazing how few lines of code I can write before I make an error.

 
Campbell Ritchie
Marshal
Posts: 56599
172
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I would have thought that a List would be a better way to store those numbers than an array; it is more expensive in terms of performance, maybe 1ns per number. It is also more expensive in terms of memory, maybe 100 bytes more for 40 numbers, out of 500 MB available. Those costs are trivial. And you can reduce the memory use of some sorts of Lists: look here.
 
Piet Souris
Master Rancher
Posts: 2044
75
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Why not go the modern (Java 8) way? Use stream() and a filter, or even more nice:
use the GroupingBy method, creating a map with keys true and false.
Sort of the partition method in Scala.

But unfortunately: having Windows XP, I can't install Java 8, so can't try it out.

Greetz,
Piet
 
Kat Rollo
Ranch Hand
Posts: 62
Eclipse IDE Java MySQL Database
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Winston Gutkowski wrote:I much prefer W.A. Wulf's quote:
"More computing sins are committed in the name of efficiency (without necessarily achieving it) than for any other single reason — including blind stupidity."

I think this is a nice programming quote to live by, thank you!

@Mike:
The method must accept a String input, but I can see the "readability" issue.

@Campbell:
Thanks, it looks like I will pretty much stick to my original approach with the List<Integer> then.
But this experiment was interesting, I even got to see a format of the for-loop I haven't seen before.

Piet Souris wrote:Why not go the modern (Java 8) way?

I asked my prof about this too ("Should we upgrade to Java 8?"), but he said he advocates for "version minus one".
We'll most likely go with Java 8 after the release of Java 9, because that's how companies do it he says.
 
Mike Simmons
Ranch Hand
Posts: 3090
14
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Well, here's a Java 8 version for fun:
 
Winston Gutkowski
Bartender
Posts: 10575
66
Eclipse IDE Hibernate Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Mike Simmons wrote:Well, here's a Java 8 version for fun:

Oddly enough, like you and Piet, I was thinking that there must be a way to do it with Streams; but I was missing the chars() bit - probably because it's defined in CharSequence (which actually makes a lot of sense).

Thanks for that.

Winston
 
Campbell Ritchie
Marshal
Posts: 56599
172
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Kat Rollo wrote: . . .
@Campbell:
. . .
But this experiment was interesting, I even got to see a format of the for-loop I haven't seen before.
It is always worth experimenting and seeing new things. You also saw versions of the for loop nobody has ever seen before
. . .
Piet Souris wrote:Why not go the modern (Java 8) way?
. . .
We'll most likely go with Java 8 after the release of Java 9, because that's how companies do it he says.
When I went to Devoxx people were saying you should upgrade straight to Java8 because of the sort of thing MS showed. Maybe better to use minor version minus one; since Java8 is now on its second upgrade (“8u11”) it would be worth trying J8.
 
Campbell Ritchie
Marshal
Posts: 56599
172
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Mike Simmons wrote:Well, here's a Java 8 version for fun:
Since the methods of the Streams API mostly work on lazy execution, you will find that produces an array of exactly the right size and does not use any more memory or processor resources than the older way of doing it.

By the way, KR, did you ever work out why n % 2 == 1 works for chars and can fail miserably for ints?
 
Mike. J. Thompson
Bartender
Posts: 689
17
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I haven't seen any survey results, but I doubt companies in general will wait until Java 9 before using Java 8. Certainly I have been using Java 7 for many years at work, we didn't just move this year. I think it will be a case of the next time I start a new project from scratch, Java 8 will be considered.

The thing that would make me stick with Java 7 would be support from third party static analysis tools. If they haven't caught up with the big syntactical changes then the benefits of Java 8 may be outweighed for me.
 
Mike Simmons
Ranch Hand
Posts: 3090
14
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Winston Gutkowski wrote:
Mike Simmons wrote:Well, here's a Java 8 version for fun:

Oddly enough, like you and Piet, I was thinking that there must be a way to do it with Streams; but I was missing the chars() bit - probably because it's defined in CharSequence (which actually makes a lot of sense).

Yes, finding that chars() method was by far the hardest part about writing that code.
 
Winston Gutkowski
Bartender
Posts: 10575
66
Eclipse IDE Hibernate Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Mike. J. Thompson wrote:The thing that would make me stick with Java 7 would be support from third party static analysis tools. If they haven't caught up with the big syntactical changes then the benefits of Java 8 may be outweighed for me.

For me, I think I might hold off on making heavy use of Streams just yet, at least until the "pipelining" aspect has been thoroughly tried and tested by braver folk than me, and there's a bit more expert information around about when to use it and when not.

I'll certainly be thinking about it though, because I love the idea in principle. It reminds me of writing bash scripts...

Winston
 
Kat Rollo
Ranch Hand
Posts: 62
Eclipse IDE Java MySQL Database
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Campbell Ritchie wrote:By the way, KR, did you ever work out why n % 2 == 1 works for chars and can fail miserably for ints?

Hi there,

I have been trying to think what you meant with the "challenge" you posted earlier.
I tried 123456 and it did extract 1, 3, 5 and placed them in the List<Integer> as intended.
Could you give me a test userInput to illustrate what you mean?
 
Winston Gutkowski
Bartender
Posts: 10575
66
Eclipse IDE Hibernate Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Kat Rollo wrote:I tried 123456 and it did extract 1, 3, 5 and placed them in the List<Integer> as intended.
Could you give me a test userInput to illustrate what you mean?

-3. However, as Campbell said, it will work fine for characters; but not for any other primitive type.
To see why, print out the result of -3 % 2. It may not be what you think it is.

And just FYI:
(n & 1) == 1
works as an "odd" test for ANY primitive integer type n, regardless of its value. I'll leave you to work out why.

Winston
 
Kat Rollo
Ranch Hand
Posts: 62
Eclipse IDE Java MySQL Database
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I haven't really used bitwise operations, so I'll admit (n & 1) == 1 is not so clear for me, as opposed to (n % 2) == 1. Sorry.
However, I found this, but it seems it became a "battle" in the answers/comments section regarding optimization.

There was an explanation for (n & 1) == 0, which stated:
Zero all the bits but leave the least significant bit unchanged and check if the result is 0

Here's my try:

(n & 1) == 0
n = 10
10 = 00001010
00001010 ^ 00000001 = 00000000
The result is 0, therefore, even.

(n & 1) == 1
n = 5
5 = 00000101
00000101 ^ 00000001 = 00000001
The result is 1, therefore, odd.

Is it correct, Sir Winston?
 
Stephan van Hulst
Saloon Keeper
Posts: 7993
143
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
That is correct, but I would dissuade you from using this method, because it only works for even/odd. The general case is that a number is divisible by some other number. You can test that for any divisor (not just 2) using the modulus operator. It will also be clear to anybody reading your code what you're doing:
 
Stephan van Hulst
Saloon Keeper
Posts: 7993
143
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
The discussion about Streams has moved here.
 
It is sorta covered in the JavaRanch Style Guide.
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!