美文网首页
ITEM 58: 先考虑使用 for-each ,再考虑 for

ITEM 58: 先考虑使用 for-each ,再考虑 for

作者: rabbittttt | 来源:发表于2019-12-22 16:50 被阅读0次

ITEM 58: PREFER FOR-EACH LOOPS TO TRADITIONAL FOR LOOPS
  正如在 item 45 中所讨论的,一些任务最好通过流来完成,而另一些任务则通过迭代来完成。这是一个传统的 for 循环遍历一个集合:

for (Iterator<Element> i = c.iterator(); i.hasNext(); ) { 
  Element e = i.next();
  ... // Do something with e
}

  这是一个传统的 for 循环来遍历一个数组:

// Not the best way to iterate over an array!
for (int i = 0; i < a.length; i++) { 
  ... // Do something with a[i]
}

  这些习惯用法比 while 循环更好(item 57),但是它们并不完美。迭代器和索引变量都很混乱——您所需要的只是元素。此外,它们代表了犯错的机会。迭代器在每个循环中出现三次,索引变量出现四次,这使您有很多机会使用错误的变量。如果这样做,就不能保证编译器会捕捉到问题。最后,这两个循环非常不同,引起了对容器类型不必要的注意,并且增加了更改该类型的小麻烦。
  for-each循环(官方称为“enhanced for语句”)解决了所有这些问题。它通过隐藏迭代器或索引变量来避免混乱和出错的机会。由此产生的习惯用法同样适用于集合和数组,简化了将容器的实现类型从一种转换为另一种的过程:

// The preferred idiom for iterating over collections and arrays
for (Element e : elements) {
  ... // Do something with e 
}

  当您看到冒号(:)时,将其读作“in”。因此,上面的循环读作“对于元素中的每个元素e”。“使用 for-each 循环不会造成性能损失,即使对于数组也是如此:它们生成的代码本质上与手工编写的代码相同。
  当涉及到嵌套迭代时,for-each 循环相对于传统 for 循环的优势甚至更大。下面是人们在进行嵌套迭代时经常犯的一个错误:

// Can you spot the bug?
enum Suit { CLUB, DIAMOND, HEART, SPADE }
enum Rank { ACE, DEUCE, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT,
NINE, TEN, JACK, QUEEN, KING } ...

static Collection<Suit> suits = Arrays.asList(Suit.values()); 
static Collection<Rank> ranks = Arrays.asList(Rank.values());

List<Card> deck = new ArrayList<>();
for (Iterator<Suit> i = suits.iterator(); i.hasNext(); )
  for (Iterator<Rank> j = ranks.iterator(); j.hasNext(); ) 
    deck.add(new Card(i.next(), j.next()));

  如果你没有发现错误,不要感到难过。许多专业程序员都曾犯过这样或那样的错误。问题是,对于外部集合(suits),下一个方法在迭代器上调用了太多次。它应该从外部循环调用,这样每花色就会调用一次,但它是从内部循环调用的,所以每牌只调用一次。当suit用完后,循环抛出NoSuchElementException。
  如果您非常不幸,并且外部集合的大小是内部集合大小的倍数(可能因为它们是相同的集合),循环将正常终止,但是它不会执行您想要的操作。举个例子,考虑一下打印一对骰子所有可能的掷骰结果的错误尝试:

// Same bug, different symptom!
enum Face { ONE, TWO, THREE, FOUR, FIVE, SIX } ...

Collection<Face> faces = EnumSet.allOf(Face.class);
for (Iterator<Face> i = faces.iterator(); i.hasNext(); ) 
  for (Iterator<Face> j = faces.iterator(); j.hasNext(); )
    System.out.println(i.next() + " " + j.next());

  程序不会抛出异常,但它只打印6个“double”(从“ONE ONE”到“six six”),而不是预期的36个组合。
  要修复这些例子中的错误,您必须在外层循环的范围内添加一个变量来保存外层元素:

// Fixed, but ugly - you can do better!
for (Iterator<Suit> i = suits.iterator(); i.hasNext(); ) { 
  Suit suit = i.next();
  for (Iterator<Rank> j = ranks.iterator(); j.hasNext(); ) 
    deck.add(new Card(suit, j.next()));
}

  如果使用嵌套的for-each循环,问题就会消失。结果的代码是简洁的,因为你可以希望:

// Preferred idiom for nested iteration on collections and arrays
for (Suit suit : suits)
  for (Rank rank : ranks) 
    deck.add(new Card(suit, rank));

  不幸的是,有三种常见的情况你不能使用for-each:
  * 破坏性筛选 —— 如果你需要遍历一个集合来删除选定的元素,那么你需要使用一个显式的迭代器来调用它的删除方法。通过使用 Java 8 中添加的 Collection 的 removeIf 方法,通常可以避免显式遍历。
  * 转换 —— 如果需要遍历一个列表或数组,并替换其中部分或全部元素的值,那么需要使用列表迭代器或数组索引来替换元素的值。
  * 并行迭代 —— 如果您需要并行地遍历多个集合,那么您需要显式地控制迭代器或索引变量,以便所有的迭代器或索引变量都可以同步进行(正如上面的错误卡片和骰子示例中无意中演示的那样)。
  如果您发现自己处于这些情况中的任何一种,请使用普通的for循环,并小心本项目中提到的陷阱。for-each 循环不仅允许您遍历集合和数组,还允许您遍历实现 Iterable接口的任何对象,该接口由单个方法组成。界面如下:

public interface Iterable<E> {
// Returns an iterator over the elements in this iterable 
  Iterator<E> iterator();
}

  如果你不得不从头开始编写自己的迭代器,Iterable 有点难以实现。但是如果你正在写一个类型代表一组元素,你绝对应该考虑把它实现 Iterable,即使你选择不实现集合。这将允许用户使用 for-each 循环遍历类型,他们将永远感激不尽。
总之,与传统的 for 循环相比,for-each 循环在清晰度、灵活性和 bug 预防方面提供了引人注目的优势,并且没有性能损失。尽可能使用 for-each 循环而不是 for loops。

相关文章

  • ITEM 58: 先考虑使用 for-each ,再考虑 for

    ITEM 58: PREFER FOR-EACH LOOPS TO TRADITIONAL FOR LOOPS  ...

  • 提示58-59

    第58条 for-each循环优先于传统的for循环 for-each 相较于使用迭代器或者传统for循环遍历数组...

  • 6-2 先静后动,构建新闻详情页面样式

    知识点1、前端代码书写,先静后动,先样式再数据。 先考虑静态标签,在考虑js脚本。前端样式写好了之后,再考虑从服务...

  • 喂 请你再考虑考虑

    有时候会想啊 学历学校好像没有那么重要 重要的是你以后会过什么样的生活和你这个人的个人能力怎么样 但看到周围那些人...

  • 2021-12-15

    先升7再考虑其他问题

  • 先后

    先影响能影响的,再考虑扩大影响。 先做好能做的,再做想做的。 先活着,再发展。 先考虑先后,再说其他。

  • 2018-11-01

    先懂我,再考虑爱不爱我? ------先懂我,再考虑爱不爱我------ 服装店的老板,开始做起了POS机业务,你...

  • 考虑考虑考虑考虑

    5句图图他

  • item 14: 考虑实现 COMPARABLE

    ITEM 14: CONSIDER IMPLEMENTING COMPARABLE  Object 中 并没有定义...

  • 先做,再考虑如何做好

    到现在,我都没学会如何有效地去读一本书;对于别人嘴里说的速读等等方法,依然不懂。照着目前的状态来看,能一周保持读一...

网友评论

      本文标题:ITEM 58: 先考虑使用 for-each ,再考虑 for

      本文链接:https://www.haomeiwen.com/subject/qwvonctx.html