views:

168

answers:

10

Hi,

I'm always struggling with something like the following Java example:

String breads[] = {"Brown", "White", "Sandwich"};
int count[] = new int[breads.length];
for (int i = 0; i < ****; i++)
{
   // Prompt the number of breads
}

****: which array.length should I choose?
I can choose between breads.length and count.length
I know it would be the same result, but I don't know which one I shoud choose.

There are many other examples where I get the same problem.
I'm sure that you have encountered this problem as well in the past.

What should you choose? Are there general agreements?

Thanks

A: 

Use Lists, Sets and Maps and foreach loops.

If you really have to use arrays and can't use foreach loops, it doesn't matter.

Jens Schauder
A foreach loop can't iterate over two collections at once.
Michael Myers
True, but in many cases just adding the elements at the end of a list is sufficient. In other cases using the Elements of the first Collections as keys and the values of the seconds as values in a Map work well.
Jens Schauder
+5  A: 

Using breads.length is more precise since count size depends on breads length, but apart from that the different results will be the same.. and this should be true whenever you operate in a way similar to the one you wrote.

If you really don't want to be annoyied by this metaphysical questions you can use a simple class:

class Bread
{
    String name;
    int count
}

or you can use a multidimensional array, or also an hashmap:

HashMap<String, Integer> counts

so you can iterate on real collection while gathering values from the map:

for (String b : breads)
   System.out.println(b + " count: "+counts.get(b));
Jack
I don't think "precise" is really the right word. Maybe "direct". Anyway, +1 for the Bread class. Parallel arrays are a sign that you really want an array of records, and objects are the way to do records in an OOP language.
Laurence Gonsalves
Yes, precise is not the right word but english is not my mother language so it happens to not be *precise* as I would :)
Jack
The problem with making a class for something like this is what happens when you want to pass either just the bread array or just the count array to a generic library function? For interfacing with generic code, an array of primitives is almost always easier to work with in most languages.
dsimcha
That's way Groovy exists, you can do breads.collect{ it.count } without any problem, just some overhead..
Jack
A: 

It depends what you are doing, but ultimately this is rarely going to be a significant decision (since the value is the same). Perhaps worry about "bigger" problems. For interest, in .NET the JIT (not the compiler) can spot this pattern and eliminate the out-of-bounds checks, but only if it is something obvious like:

for(int i = 0 ; i < arr.Length ; i++) {
    Console.WriteLine(arr[i]); // no bounds check if the JIT can prove it will
                               // never fail
}
Marc Gravell
+1  A: 

In this scenario, I would go with breads because that's how you would explain the code.

Logically, you are iterating over each type of bread, not the number of counts of breads (even though it is the same).

Imagine you are explaining your code to someone else, and use whatever makes sense from that perspective. Sometimes both make sense, in which case you just have to choose arbitrarily.

Peter Alexander
+6  A: 

I think I understand your question.

The answer is don't use arrays.

In this case use a map of:

Map<String, Integer> breadCount = new TreeMap<String, Integer>();

breadCount.put("Brown", 0);
breadCount.put("White", 0);
breadCount.put("Sandwich", 0);

Then there's only one "length", which is breadCount.size()

Example is in Java

Pyrolistical
In similar spirit, you could create a class that captures both the String and the Integer value, so again, there is only one collection (I know class Map is not a Collection)
daveb
Map is not a Collection, but you can do a "for each" loop over it. It ends up being a little wordy, but that's alright. `for (Entry<String, Integer> entry : breadMap.entrySet()) {`... and then the bread name is `entry.getKey()` and the count is `entry.getValue()`.
MatrixFrog
Disagree. A map only makes sense when you need to be able to look things up rather than iterating in order. If not, an array is more space efficient and is a nice lowest common denominator data structure that is more likely than a map to be accepted by generic library functions.
dsimcha
Source code is for people not computers.
Pyrolistical
A: 

I'd say choose the "main" array and use its length everywhere. This way your code is clearer and easier to read. Other than that, I think it doesn't really matter, because count.length will be the same and you could mix these two as you like.

pajton
A: 

This problem seams to be rather accademic -- but in your case, I would prefer the array that you intent to change (count), since when in some (rather accademic) case, the length of breads and count should differ, it is always best to not run into array bounds trouble ...

Still there could be a differing solution: Have only one array -- containing structures with name ("breads" could be also called "breadNames") and count ...

Juergen
Why does your approach prevent array bounds trouble? In order to achieve that you would have to use something like the minimum of the two length. Which I would consider code obfuscation.
Jens Schauder
Because it looked to me as only the count array would be used in the loop. "prompt the number of breads" -- and (as much I understood the problem) would be a loop that inputs the counts ... so only the count array would be touched.
Juergen
+1  A: 

In such a situation I declare a local variable. Like this:

int n = breads.length;
int[] count = new int[n];
for (int i = 0; i < n; i ++) {
    // ...
}

Conceptually, this means that I make the "loop count" a value in its own right. A side effect is that your problem is solved.

Thomas Pornin
A: 

I would try to avoid having the different arrays altogether. More often than not, if you have two arrays that have the same size at all times you are actually faking a compound type or a relationship.

// C++: inventory map:
std::map< std::string, int > bread_inventory;
bread_inventory["White"] = 0;
bread_inventory["Brown"] = 0;
bread_inventory["Sandwich"] = 0;

// C++: complex type:
struct bread_type
{
   bread_type( std::string const & name, int calories )
      : name(name), calories(calories) {}
   std::string name;
   int calories;
};
std::vector<bread_type> v;
v.push_back( bread_type("White", 100) );
v.push_back( bread_type("Brown", 90) );

By using concrete types or containers (as compared to unrelated elements in different containers) you get coherence for free (less care must be taken to verify that both containers get updated in the same way) and some operations become much simpler (ordering the vector by calories can be trivially implemented with an ordering function, while if you keep two separate vectors for the names and the associated values you would have to fight your way to guarantee that the same operations are performed on both containers.

The code is also easier to follow and maintain as the types are telling the programmer what the writer wanted. Part of the knowledge of the domain that you have when writing the class is lost in your implementation. Your implementation is dealing with an array of bread types and one array of counts, but the code does not tell you that they are in fact one single entity.

David Rodríguez - dribeas
A: 

You are using for(int i=0; i<array.length; i++) to iterate over the values of the array. So you actually wants to use array[i] and not something else. In your case you want to iterate over the names which you have defined. The count array however is filled with zeros and the question is why you want to iterate over them. So if you write

String breads[] = {"Brown", "White", "Sandwich"};
int count[] = new int[breads.length];
for (int i = 0; i < breads.length; i++) {
}

you clearly see that the loop is for iterating over the names. And you deal with these names (like showing them). But if you write

String breads[] = {"Brown", "White", "Sandwich"};
int count[] = new int[breads.length];
for (int i = 0; i < count.length; i++) {
}

The question rise why do you want to iterate over the count values? they are all 0. The data flow actually is something like count_value = f(bread_value), the bread names are the independent variable. You can write (in Java)

String breads[] = {"Brown", "White", "Sandwich"};
int count[] = new int[breads.length];
for (String bread: breads) {
}

and clearly sees that you care about the bread names and wants to do something with them. That you want to set the counts are just a side affect of the loop, but it doesn't control the times of iterations.

Progman