【问题标题】:use List Foreach instead of for() Java使用 List Foreach 而不是 for() Java
【发布时间】:2017-03-18 04:20:40
【问题描述】:

我目前正在学习 Java,我有兴趣学习如何使用 list foreach 方法,而不是使用手动构造的 for 循环。我怀疑他们可以执行相同的操作。

我当前的代码如下所示:

while(!this.cards.isEmpty()) {
    for(int i = 0; i < this.table.GetPlayerCount()  && !this.cards.isEmpty(); i++) {
    DealOneCardToPlayer(this.table.players.get(i) ,this.cards.get(0));
    this.cards.remove(0);
    }
}

我怀疑有一种方法可以使用这样的东西:

this.cards.foreach() { do suff }

但我还没有完全确定语法...有人可以帮我快速解决吗?

【问题讨论】:

  • for(...) 中的额外条件意味着这不是一个简单的for 循环。如果您正在尝试学习如何使用 forEach,这不是学习的最佳示例。
  • 不过,您真的不需要在循环中移除卡片。如果你改用this.cards.iterator(),你可以简单地得到下一张卡,而不用移除它们(然后你可以在之后调用this.cards.clear(),如果你愿意的话)。
  • 好吧,我正在尝试学习如何对“现实世界”进行建模——所以当庄家从“套牌”中发牌时,这就是这个循环正在做的事情。当一张牌被发给玩家时,它会从“套牌”中“移除”。我想我想保留这一点 - 代码正在工作 - 我只是在寻找更简洁的表达方式,我认为 foreach 就是这样。 @user902383 为什么你说你不认为我可以在这里使用 foreach?

标签: java list foreach


【解决方案1】:

我认为你可以通过迭代卡片的索引来做到这一点:

IntStream.range(0, this.cards.size()).forEach(idx -> {
    DealOneCardToPlayer(
        this.table.players.get(idx % this.table.GetPlayerCount()),
        this.cards.get(idx));
});

虽然这不会在你移动时移除卡片;如果你真的需要 this.cards 之后为空:

this.cards.clear();

如果你想限制发牌的数量(例如你想给每个玩家发 N 张牌),最简单的方法是提取一个子列表,然后只需应用上述相同的方法:

List<Card> cardsToDeal = this.cards.subList(0, numCardsToDeal);
IntStream.range(0, cardsToDeal.size()).forEach(idx -> {
    DealOneCardToPlayer(
        this.table.players.get(idx % this.table.GetPlayerCount()),
        cardsToDeal.get(idx));
});
cardsToDeal.clear();

【讨论】:

  • 嗯,是的——我确实需要卡片的“套牌”来移除“发牌”的卡片。然而,虽然目前这不能处理比​​“套牌”更少的牌,但它最终会处理。所以我不确定 this.cards.clear();将是正确的举动......想法?
  • 我不明白您认为存在的问题 - 这处理的卡片数量与卡片数量一样多(假设卡片没有同时在另一个线程中被修改)。因此,如果您想从牌组中“移除”它们,迭代它们然后清除牌组与在迭代它们时移除每张牌具有相同的效果。
  • 正确 - 当前迭代器从牌组中移除所有卡片。然而,在未来,一旦我编码它,它将向每个玩家发一定数量的牌(由不同的方法确定)。发完这些牌后,牌组中就会剩下一些牌。目前,这只是处理套牌中的每张牌,仅此而已。
  • 然后执行IntStream.range(0, numPlayers * numCardsPerPlayer)this.cards.subList(0, numPlayers * numCardsPerPlayer).clear()。或者,List&lt;Card&gt; subCards = this.cards.subList(0, numPlayers * numCardsPerPlayer),然后将所有对this.cards 的引用替换为subCards。 (当然,你还需要保证numPlayers * numCardsPerPlayer &lt;= this.cards.size())。
  • 我需要导入什么才能使用 InStream?
【解决方案2】:

您说的是 Java 8 的函数 API(和 lambdas)。

本质上 lambda 是函数/方法的速记兄弟,它们有输入和可能的返回值。对于#forEach,它要求您提供一个接受T(您的列表类型)的函数,并且不返回任何内容。这称为Consumer。然后该方法获取您提供的Consumer,并为列表中的每个元素调用它。

对于等价性,就您在开发时所关心的而言,这些本质上是相同的:

void someConsumerMethod(Card c) {
    //lambda code block
}


(Card c) -> //lambda code block


this::someConsumerMethod //direct reference to the first method

一个例子是:

this.cards.forEach(c -> {
    System.out.println(c); //fully expanded
});
this.cards.forEach(c -> System.out.println(c)); //shorthand / one-liner
//or, since println already matches a Consumer<Object>, we can method reference it!
this.cards.forEach(System.out::println);

至于调整您的示例,我不建议您在迭代时修改集合(至少,不使用Iterator#remove)。 Andy Turner 的回答已经向您展示了如何使用IntStream 的应用程序来迭代您想要的索引。

【讨论】:

  • 是的,我认为 Lambdas 是我感兴趣的。我会尽快试一试,看看我想出了什么。谢谢 Rogue。
  • 你也应该复习一下函数 api,因为它们在 lambdas 的“接收”端非常重要。
【解决方案3】:

您正在寻找的是 lambda 表达式。 forEach 方法接受一个函数式接口(一个带有一个抽象方法的接口),这意味着它可以按如下方式使用:

  List<String> myList;
  myList.forEach((String s) -> System.out.println(s));

来源:http://www.oracle.com/webfolder/technetwork/tutorials/obe/java/Lambda-QuickStart/index.html

【讨论】:

    【解决方案4】:

    你的意思是你想在你的 List 中使用新的 Java 8 流。

    所以语法是这样的:

    this.cards.stream().forEach(card -> {// do something here});
    

    您可以调用 stream() 来访问方法 forEach()

    或直接使用您的列表:

    this.cards.forEach(...);
    

    在我的示例中,我使用了新的 Java 8 箭头函数,这是一种避免匿名类的样板的便捷方法(很大程度上是一种演变)。

    您可以将方法传递给forEach()

    this.cards.stream().forEach(doSomethingWithMyCard);
    

    【讨论】:

    • 一个列表(任何Iterable甚至)直接实现forEach,不需要使用stream().forEach(...)
    • 其实Iterable包含了方法,Map也有#forEach也就是BiConsumer&lt;K, V&gt;
    【解决方案5】:

    您可以跟踪当前玩家索引,并遍历卡包,以完全分发它:

    int playerIndex = 0;
    
    for(Card card : cards){
    
      DealOneCardToPlayer(this.table.players.get(playerIndex) , card);
    
      if(playerIndex == this.table.GetPlayerCount()-1)
        playerIndex = 0;
      else
        playerIndex = playerIndex + 1;
    
    
    }
    

    如果您只想为每个玩家分配一张牌,请替换

      if(playerIndex == this.table.GetPlayerCount()-1)
        playerIndex = 0;
      else
        playerIndex = playerIndex + 1;
    

    与:

      if(playerIndex == this.table.GetPlayerCount()-1)
        break;
    

    【讨论】:

      【解决方案6】:

      您的示例并不真正适合转换为 forEach 调用,因为您在其中有所有这些额外的条件和东西。您还在 for 循环中修改列表。由于这些原因,您最好坚持使用 for 循环。

      当您想要改变列表中的对象,或者您只想访问列表中的每个项目时,可以使用 forEach 调用。

      语法如下:

      list.forEach(item -> <insert the stuff you want to do to the item here>);
      

      这里有一些例子展示了如何使用forEach。我还提供了等效的 for 循环:

      // forEach
      list.forEach(item -> item.setSomeProperty(1));
      // for loop
      for (int i = 0 ; i < list.size() ; i++) {
          list.get(i).setSomeProperty(1);
      }
      
      // forEach
      list.forEach(item -> item.doStuff());
      // for loop
      for (int i = 0 ; i < list.size() ; i++) {
          list.get(i).doStuff()
      }
      
      // forEach
      list.forEach(item -> System.out.println(item));
      // for loop
      for (int i = 0 ; i < list.size() ; i++) {
          System.out.println(list.get(i));
      }
      

      希望您可以通过查看上面的示例来理解语法。

      注意最后一个例子可以简化为:

      list.forEach(System.out::println);
      

      我们基本上是告诉它在每个项目上调用System.out::println

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-01-02
        • 2021-11-01
        • 2010-12-21
        • 2023-03-29
        • 1970-01-01
        • 1970-01-01
        • 2012-01-27
        • 2019-02-05
        相关资源
        最近更新 更多