Итераторы в C ++ (stl) против Java, есть ли концептуальная разница? - PullRequest
30 голосов
/ 11 сентября 2008

Я возвращаюсь на с ++ после того, как нахожусь немного и пытаюсь стереть старую дыню.

В Java Iterator представляет собой интерфейс для контейнера, имеющий методы: hasNext (), next () и remove (). Наличие hasNext () означает, что имеет концепцию ограничения для проходимого контейнера.

//with an Iterator
Iterator<String> iter = trees.iterator();
while (iter.hasNext()) 
{
    System.out.println(iter.next());
}

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

vector<int> vec;
vector<int>::iterator iter;

// Add some elements to vector
v.push_back(1);
v.push_back(4);
v.push_back(8);

for(iter= v.begin(); iter != v.end(); iter++)
{
    cout << *i << " "; //Should output 1 4 8
}

Интересно, что в C ++ указатель является итератором массива. STL взял то, что существовало, и построил соглашение вокруг этого.

Есть ли еще какая-то тонкость в этом, что я пропускаю?

Ответы [ 9 ]

20 голосов
/ 11 сентября 2008

Возможно, немного более теоретический. Математически коллекции в C ++ можно описать как полуоткрытый интервал итераторов, а именно один итератор, указывающий на начало коллекции, и один итератор, указывающий сразу за последним элементом.

Это соглашение открывает множество возможностей. То, как алгоритмы работают в C ++, все они могут применяться к подпоследовательностям большей коллекции. Чтобы заставить это работать в Java, вы должны создать оболочку вокруг существующей коллекции, которая возвращает другой итератор.

Фрэнк уже упомянул еще один важный аспект итераторов. Существуют разные концепции итераторов. Java-итераторы соответствуют входным итераторам C ++, то есть являются итераторами только для чтения, которые могут увеличиваться только на один шаг за раз и не могут возвращаться назад.

С другой стороны, у вас есть C-указатели, которые точно соответствуют C ++ концепции итератора с произвольным доступом.

В целом, C ++ предлагает гораздо более богатую и чистую концепцию, которая может быть применена к гораздо более широкому кругу задач, чем указатели C или итераторы Java.

18 голосов
/ 11 сентября 2008

Да, есть большая концептуальная разница. C ++ использует разные «классы» итераторов. Некоторые используются для произвольного доступа (в отличие от Java), некоторые используются для прямого доступа (например, Java). В то время как даже другие используются для записи данных (например, для transform).

См. Концепцию итераторов в документации C ++ :

  • Итератор ввода
  • Итератор вывода
  • Прямой итератор
  • Двунаправленный итератор
  • Итератор произвольного доступа

Они гораздо более интересные и мощные по сравнению с маленькими итераторами Java / C #. Надеемся, что эти соглашения будут кодифицированы с использованием C ++ 0x Concepts .

11 голосов
/ 02 октября 2008

Как уже упоминалось, итераторы Java и C # описывают смешанное положение (состояние) и диапазон (значение), в то время как итераторы C ++ разделяют понятия положения и диапазона. Итераторы C ++ представляют «где я сейчас» отдельно от «куда мне идти?».

Итераторы Java и C # не могут быть скопированы. Вы не можете восстановить предыдущую позицию. Обычные итераторы C ++ могут.

Рассмотрим этот пример :

// for each element in vec
for(iter a = vec.begin(); a != vec.end(); ++a){
  // critical step!  We will revisit 'a' later.
  iter cur = a; 
  unsigned i = 0;
  // print 3 elements
  for(; cur != vec.end() && i < 3; ++cur, ++i){
      cout << *cur << " ";
  }
  cout << "\n";
}

Нажмите на ссылку выше, чтобы увидеть вывод программы.

Этот довольно глупый цикл проходит последовательность (используя только семантику прямого итератора), печатая каждую смежную подпоследовательность из 3 элементов ровно один раз (и пару более коротких подпоследовательностей в конце). Но если предположить N элементов и M элементов на строку вместо 3, этот алгоритм все равно будет иметь O (N * M) приращений итераторов и O (1) пробел.

Итераторам в стиле Java не хватает возможности сохранять позиции независимо. Вы будете либо

  • потерять O (1) место, используя (например) массив размера M для хранения истории при итерации
  • нужно будет пройти по списку N раз, делая O (N ^ 2 + N * M) времени
  • или используйте конкретный тип Array с функцией-членом GetAt, потеряв универсальность и возможность использовать типы контейнеров связанных списков.

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

Неспособность сохранить состояние наиболее точно соответствует входному итератору C ++ STL, на котором построено очень мало алгоритмов.

7 голосов
/ 11 сентября 2008

Указатель на элемент массива действительно является итератором в массиве.

Как вы говорите, в Java итератор знает больше о базовом контейнере, чем в C ++. Итераторы C ++ являются общими, и пара итераторов может обозначать любой диапазон: это может быть поддиапазон контейнера, диапазон между несколькими контейнерами (см. http://www.justsoftwaresolutions.co.uk/articles/pair_iterators.pdf или http://www.boost.org/doc/libs/1_36_0/libs/iterator/doc/zip_iterator.html) или даже диапазон чисел (см. http://www.boost.org/doc/libs/1_36_0/libs/iterator/doc/counting_iterator.html)

Категории итераторов определяют, что вы можете и не можете делать с данным итератором.

3 голосов
/ 11 сентября 2008

Для меня принципиальное отличие заключается в том, что итераторы Java указывают на элементы, а итераторы C ++ STL указывают на элементы.

2 голосов
/ 12 сентября 2008

C ++ итераторы являются обобщением концепции указателя; они делают его применимым к более широкому кругу ситуаций. Это означает, что они могут использоваться для таких вещей, как определение произвольных диапазонов.

Java-итераторы - относительно глупые перечислители (хотя и не такие плохие, как C #; по крайней мере, в Java есть ListIterator, и его можно использовать для изменения коллекции).

1 голос
/ 30 апреля 2015

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

В C ++ у вас есть метод для продвижения итератора и чтения текущего значения. Чтение его значения не продвигает итерацию; так что вы можете прочитать его несколько раз. Это невозможно с Java-итераторами, и я в итоге создаю оболочки, которые делают это.

Примечание: одним из простых способов создания обертки является использование существующей - PeekingIterator из Гуавы.

1 голос
/ 11 сентября 2008

Итераторы библиотеки C ++ (ранее известная как STL) разработаны для совместимости с указателями. Java, без арифметики указателей, была свободна для программистов.

В C ++ вам приходится использовать пару итераторов. В Java вы используете итератор или коллекцию. Предполагается, что итераторы являются связующим звеном между алгоритмом и структурой данных. Код, написанный для 1.5+, редко требует упоминания итераторов, если только он не реализует определенный алгоритм или структуру данных (что большинству программистов не требуется) Поскольку в Java используются подмножества динамического полиморфизма и тому подобное, с ними гораздо проще работать.

1 голос
/ 11 сентября 2008

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

...