Когда я перебираю два массива одновременно, какой из них я использую в качестве ограничения? - PullRequest
7 голосов
/ 08 марта 2010

Я всегда борюсь с чем-то вроде следующего примера Java:

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

****: какую массивную длину мне выбрать?
Я могу выбрать между breads.length и count.length
Я знаю, что это будет тот же результат, но я не знаю, какой из них я выберу.

Есть много других примеров, когда я получаю ту же проблему.
Я уверен, что вы сталкивались с этой проблемой и в прошлом.

Что выбрать? Существуют ли генеральные соглашения?

Спасибо

Ответы [ 10 ]

6 голосов
/ 08 марта 2010

Мне кажется, я понимаю ваш вопрос.

Ответ - не использовать массивы.

В этом случае используйте карту:

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

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

Тогда есть только одна "длина", которая равна breadCount.size()

Пример на Java

5 голосов
/ 08 марта 2010

Использование breads.length является более точным точным , поскольку размер count зависит от длины breads, но, кроме того, разные результаты будут одинаковыми ... и это должно быть верно, когда вы работаете в способ похож на тот, который вы написали.

Если вы действительно не хотите, чтобы вас раздражали эти метафизические вопросы, вы можете использовать простой класс:

class Bread
{
    String name;
    int count
}

или вы можете использовать многомерный массив, или также hashmap:

HashMap<String, Integer> counts

так что вы можете перебирать реальную коллекцию, собирая значения с карты:

for (String b : breads)
   System.out.println(b + " count: "+counts.get(b));
1 голос
/ 08 марта 2010

В такой ситуации я объявляю локальную переменную. Как это:

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

Концептуально это означает, что я делаю "счетчик циклов" самостоятельным значением. Побочным эффектом является то, что ваша проблема решена.

1 голос
/ 08 марта 2010

В этом сценарии я бы выбрал breads, потому что именно так вы бы объяснили код.

Логически вы перебираете каждый тип хлеба, а не количество подсчетов (хотяэто то же самое).

Представьте, что вы объясняете свой код кому-то еще и используете все, что имеет смысл с этой точки зрения.Иногда оба имеют смысл, и в этом случае вам просто нужно выбрать произвольно.

0 голосов
/ 08 марта 2010

Вы используете for(int i=0; i<array.length; i++) для перебора значений массива. Таким образом, вы действительно хотите использовать array[i], а не что-то еще. В вашем случае вы хотите перебрать имена, которые вы определили. Массив count, однако, заполнен нулями, и вопрос в том, почему вы хотите перебирать их. Так что если вы напишите

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

вы ясно видите, что цикл предназначен для перебора имен. И вы имеете дело с этими именами (например, показывать их). Но если вы напишите

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

Возник вопрос Почему вы хотите перебрать значения счетчиков? все они 0 . Поток данных на самом деле похож на count_value = f(bread_value), имена хлебов - это независимая переменная . Вы можете написать (на Java)

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

и ясно видит, что вы заботитесь о именах хлеба и хотите что-то с ними сделать. То, что вы хотите установить count , является лишь побочным эффектом цикла, но он не контролирует время итераций.

0 голосов
/ 08 марта 2010

Я бы постарался вообще не иметь разных массивов. Чаще всего, если у вас два массива одинакового размера, вы фактически подделываете составной тип или отношение.

// 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) );

Используя конкретные типы или контейнеры (по сравнению с несвязанными элементами в разных контейнерах), вы получаете согласованность бесплатно (нужно меньше внимания уделять проверке того, что оба контейнера обновляются одинаково), а некоторые операции становятся намного проще (упорядочение вектор по калориям может быть тривиально реализован с помощью функции упорядочения, в то время как если вы сохраните два отдельных вектора для имен и связанных значений, вам придется изо всех сил пытаться гарантировать, что одни и те же операции выполняются в обоих контейнерах.

Код также легче отслеживать и поддерживать, поскольку типы сообщают программисту, чего хотел автор. Часть знаний о предметной области, которой вы располагаете при написании класса, теряется в вашей реализации. Ваша реализация имеет дело с массивом типов хлеба и одним массивом счетчиков, но код не говорит вам, что они на самом деле являются одним целым.

0 голосов
/ 08 марта 2010

Эта проблема выглядит довольно академично - но в вашем случае я бы предпочел массив, который вы намереваетесь изменить (считать), поскольку в некоторых (довольно академических) случаях длина хлебов и счет должны отличаться, всегда лучше не сталкиваться с проблемой границ массива ...

Тем не менее, может быть другое решение: иметь только один массив, содержащий структуры с именем ("хлеб" также может называться "breadNames") и счетчиком ...

0 голосов
/ 08 марта 2010

Я бы сказал, выберите «основной» массив и везде используйте его длину. Таким образом, ваш код становится понятнее и легче для чтения. Кроме этого, я думаю, что это не имеет большого значения, потому что count.length будет одинаковым, и вы можете смешать эти два, как хотите.

0 голосов
/ 08 марта 2010

Это зависит от того, что вы делаете, но в конечном итоге это редко будет значительным решением (поскольку значение одинаково). Возможно, беспокоиться о «больших» проблемах. Для интереса, в .NET JIT (, а не компилятор) может определить этот шаблон и устранить проверки за пределами границ, но только если это что-то очевидное, например:

for(int i = 0 ; i < arr.Length ; i++) {
    Console.WriteLine(arr[i]); // no bounds check if the JIT can prove it will
                               // never fail
}
0 голосов
/ 08 марта 2010

Используйте списки, наборы и карты и циклы foreach.

Если вы действительно должны использовать массивы и не можете использовать циклы foreach, это не имеет значения.

...