32

在离开一段时间并试图清除旧瓜后,我将返回 C++。

在 Java 中,Iterator 是具有以下方法的容器的接口hasNext()next()remove(). 存在hasNext()意味着它具有被遍历的容器的限制的概念。

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

在 C++ 标准模板库中,迭代器似乎表示支持的数据类型或类operator++operator==但没有内置限制的概念,因此在前进到下一项之前需要进行比较。在正常情况下,用户必须通过比较两个迭代器来检查限制,第二个迭代器是容器端。

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 采用现有的东西并围绕它建立约定。

我错过了任何进一步的微妙之处吗?

4

9 回答 9

23

也许更理论一点。在数学上,C++ 中的集合可以描述为迭代器的半开区间,即一个迭代器指向集合的开头,一个迭代器指向最后一个元素的后面。

这个约定开辟了许多可能性。算法在 C++ 中的工作方式,它们都可以应用于更大集合的子序列。要使这样的事情在 Java 中工作,您必须围绕返回不同迭代器的现有集合创建一个包装器。

Frank 已经提到了迭代器的另一个重要方面。迭代器有不同的概念。Java 迭代器对应于 C++ 的输入迭代器,即它们是只读的迭代器,一次只能递增一步,不能倒退。

在另一个极端,你有完全对​​应于 C++ 的随机访问迭代器概念的 C 指针。

总而言之,C++ 提供了比 C 指针或 Java 迭代器更丰富、更纯粹的概念,可以应用于更广泛的任务。

于 2008-09-11T12:29:48.810 回答
21

是的,存在很大的概念差异。C++ 使用不同的迭代器“类”。有些用于随机访问(与 Java 不同),有些用于前向访问(如 java)。甚至其他人也用于写入数据(例如,与 一起使用transform)。

请参阅C++ 文档中的迭代器概念:

  • 输入迭代器
  • 输出迭代器
  • 前向迭代器
  • 双向迭代器
  • 随机访问迭代器

与 Java/C# 的微不足道的迭代器相比,这些迭代器更加有趣和强大。希望这些约定将使用 C++0x 的概念进行编码。

于 2008-09-11T12:05:12.083 回答
12

如前所述,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) 时间
  • 或者使用带有 GetAt 成员函数的具体 Array 类型,失去通用性和使用链表容器类型的能力。

由于在这个例子中只使用了前向迭代机制,我能够毫无问题地交换一个列表。这对于创作通用算法至关重要,例如搜索、延迟初始化和评估、排序等。

无法保留状态最接近于 C++ STL 输入迭代器,在该迭代器上构建的算法很少。

于 2008-10-02T08:58:34.707 回答
8

指向数组元素的指针确实是数组的迭代器。

正如您所说,在 Java 中,迭代器比 C++ 更了解底层容器。C++ 迭代器是通用的,一迭代器可以表示任何范围:这可以是容器的子范围,也可以是多个容器的范围(参见http://www.justsoftwaresolutions.co.uk/articles/pair_iterators.pdfhttp://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 )

迭代器类别标识了您可以使用给定的迭代器做什么和不能做什么。

于 2008-09-11T12:23:54.377 回答
3

对我来说,根本区别在于 Java 迭代器指向项目之间,而 C++ STL 迭代器指向项目。

于 2008-09-11T15:01:01.847 回答
2

C++ 迭代器是指针概念的概括;它们使其适用于更广泛的情况。这意味着它们可以用来做诸如定义任意范围之类的事情。

Java 迭代器是相对愚蠢的枚举器(虽然没有 C# 的那么糟糕;至少 Java 有 ListIterator 并且可以用来改变集合)。

于 2008-09-12T10:48:59.277 回答
2

关于这些差异有很多很好的答案,但我觉得 Java 迭代器最让我烦恼的事情没有得到强调——你不能多次读取当前值。这在很多场景中都非常有用,尤其是在合并迭代器时。

在 c++ 中,您有一种方法可以推进迭代器并读取当前值。读取它的值不会推进迭代;所以你可以多次阅读。这对于 Java 迭代器是不可能的,我最终创建了执行此操作的包装器。

附注:创建包装器的一种简单方法是使用现有的包装器——来自 Guava的PeekingIterator 。

于 2015-04-29T21:18:40.073 回答
1

迭代器仅等效于按顺序迭代数组内容的简单情况下的指针。迭代器可以从任意数量的其他来源提供对象:来自数据库、来自文件、来自网络、来自其他一些计算等。

于 2008-09-11T12:20:26.337 回答
1

C++ 库(以前称为 STL 的部分)迭代器旨在与指针兼容。Java,没有指针算法,可以自由地对程序员更友好。

在 C++ 中,您最终不得不使用一对迭代器。在 Java 中,您可以使用迭代器或集合。迭代器应该是算法和数据结构之间的粘合剂。为 1.5+ 编写的代码很少需要提及迭代器,除非它正在实现特定的算法或数据结构(大多数程序员不需要这样做)。随着 Java 的动态多态性子集等更容易处理。

于 2008-09-11T12:41:53.413 回答