【问题标题】:Java 8 for each and first index每个索引和第一个索引的 Java 8
【发布时间】:2017-08-17 04:48:23
【问题描述】:

任何人都知道如何分别以 Java 8 方式实现以下代码片段是否有任何流方法来检测 forEach 中的第一个元素?

List<String> myList = new ArrayList<String>();
myList.add("A");
myList.add("B");

int i = 0;

for (final String value : myList) {
   if (i == 0) {
     System.out.println("Hey that's the first element");
   }

   System.out.println(value);

   i++;
 }

Java8:

myList.stream().forEach(value -> {
  // TODO: How to do something special for first element?

  System.out.println(value);
});

此外,假设目标如下(控制台输出):

A    Something special
B
C
D
E
F

【问题讨论】:

  • Java8 流是 JVM 中 函数式编程 工具的一部分。因此,流中的任何操作都应该是无状态的,但要区分第一个或任何其他元素的状态。因此,对于您想要做的事情来说,流 API 是错误的工具。
  • 这并不适合使用流和 forEach,它们适用于所有项目的处理都是同质的。您能想出的任何解决方案都是高度人为的,并且一旦您尝试使用并行流,它可能会中断。换句话说,使用for 循环。
  • @PeterParker first forEach 必须替换为 forEachOrdered。那么如果你从一个集合中流出会发生什么?不能保证 first 实际上是第一个。最正确的方法是查看 Holger 的绝对必读答案:stackoverflow.com/questions/27547519/…;这是使用自定义 Spliterator。
  • Iterator&lt;String&gt; i=myList.iterator(); if(i.hasNext()) System.out.println("first: " + i.next()); i.forEachRemaining(System.out::println);
  • @Holger 这正是我的想法。您会将其作为选项添加到答案中吗?

标签: java for-loop foreach java-8


【解决方案1】:

这对你有用吗?

myList.stream().findFirst().ifPresent(e -> System.out.println("Special: " + e));
myList.stream().skip(1).forEach(System.out::println);

输出:

Special: A
B

替代方案:

myList.stream().findFirst().ifPresent(e -> somethingSpecial(e));
myList.stream().forEach(System.out::println);

【讨论】:

  • 我宁愿使用myList.stream().findFirst().ifPresent(e -&gt; System.out.println("Special: " + e)); 而不是peek... 请注意findFirst() 的javadoc 说“如果流没有遇到顺序,则可以返回任何元素。”,但它可以用于有序列表。
  • @Oneiros 你假设myList 是一个列表,考虑到OP 想要这样,这还不错,但是如果你(不小心?)切换到Set,这将失败。正确的方法可能是检查 Spliterator 的 SIZEDSUBSIZED 属性。
  • 考虑到他想对第一个元素执行特殊操作,为什么 OP 会使用Set?集合没有顺序,在这种情况下没有任何意义。
  • @Oneiros:你说得对,特别对待无序 Set 的“第一个”元素是没有意义的,但你不会相信人们经常想要这个……
  • 我将此答案标记为解决方案,但我不得不说另一个也是有效的。我出于好奇打开了那个帖子,因为我想学习一些东西,这已经实现了。最后,主要是出于性能原因,我很可能会像往常一样使用 for,但会在其他场合使用 forEachOrdered 和 findFirst
【解决方案2】:

我认为流中没有提供任何东西。

您可以考虑多种选择:

方法一:

int[] i = {0};  // I am using it only as an int holder.
myList.stream().forEach(value -> {
  if (i[0]++ == 0) {
    doSomethingSpecial;
  }
  System.out.println(value);
});

方法二:

如果您的列表可以快速随机访问(例如ArrayList),您可以考虑:

IntStream.range(0, myList.size())
  .forEach(i -> {
    ValueType value = myList.get(i);
    if (i == 0) {
      doSomethingSpecial();
    }
    System.out.println(value);
  });

【讨论】:

    【解决方案3】:

    试试StreamEx

    StreamEx.of("A", "B", "C") //
            .peekFirst(e -> System.out.println(e + " Something special")).skip(1) //
            .forEach(System.out::println);
    

    【讨论】:

    • 谢谢,StreamEx 看起来很方便,但我的问题是关于原生 Java8
    【解决方案4】:

    如果您试图在单个流语句中实现它,我会这样做:

    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.atomic.AtomicBoolean;
    
    public class Solution {
        public static void main(String[] args) {
            List<String> myList = new ArrayList<>();
            myList.add("A");
            myList.add("B");
            myList.add("C");
            myList.add("D");
            myList.add("E");
            myList.add("F");
    
            AtomicBoolean firstElementProcessed = new AtomicBoolean(false);
            myList.stream().map(s -> {
                if (firstElementProcessed.getAndSet(true)) {
                    return s;
                }
                return (s + " Something special");
            }).forEach(System.out::println);
        }
    }
    

    也许为了更好的可读性,我会将其重构为:

    public class Solution {
            ...
    
            AtomicBoolean firstElementProcessed = new AtomicBoolean(false);
            myList.stream().map(s -> getString(firstElementProcessed, s)).forEach(System.out::println);
        }
    
        private static String getString(AtomicBoolean firstElementProcessed, String s) {
            if (firstElementProcessed.getAndSet(true)) {
                return s;
            }
            return (s + " Something special");
        }
    }
    

    【讨论】:

      【解决方案5】:

      类似

          List<String> mylist = Arrays.asList("A", "B", "C", "D", "E", "F");
          Optional<String> findFirst = mylist.stream().findFirst();
          findFirst.ifPresent(i -> System.out.println("Hey! " + i + ", that's the first element"));
          mylist.stream().skip(1).forEach(System.out::println);
      

      【讨论】:

        【解决方案6】:

        没有理由不使用简单的 for 循环,foreach 很漂亮但仅限于副作用。

        【讨论】:

        • 试试{"one", "two", "one"}的列表
        • 正如我所说,好奇和学习是我提出问题的原因
        猜你喜欢
        • 2020-09-30
        • 2018-06-22
        • 1970-01-01
        • 1970-01-01
        • 2015-03-21
        • 1970-01-01
        • 1970-01-01
        • 2021-08-28
        • 1970-01-01
        相关资源
        最近更新 更多