65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 IntStream 进行范围和循环

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2014年7月2日

CPOL

3分钟阅读

viewsIcon

11855

使用 IntStream 进行范围和循环。

在之前的文章中,我们学习了如何获取容器,将其内容流式传输到管道中,并研究了该管道中数据的几种不同操作(forEachmapfilterpeek)。

Java 8 支持几种专用流,其中管道包含特定类型的对象。今天,我们将学习IntStream,它在管道中传递整数。

public class IntStreamExample
{
	public static void main(String[] args)
	{
		System.out.println("[1,5]");
		IntStream.rangeClosed(1, 5).forEach(System.out::println);

		System.out.println("[1,5)");
		IntStream.range(1, 5).forEach(System.out::println);

		System.out.println("Just 3");
		IntStream.of(3).forEach(System.out::println);

		System.out.println("Specific values");
		IntStream.of(1, 3, 5, 6).forEach
                (System.out::println);

		System.out.println("[1,3] and [4,6] joined");
		IntStream.concat(IntStream.rangeClosed(1, 3),
		IntStream.rangeClosed(4, 6)).forEach(System.out::println);
	}
}

要构建我们的流,我们使用IntStream类并使用其一个static方法。上面的示例演示了其中一些方法。

对于错过之前文章的读者,:: 运算符表示传递右侧的函数,并使用左侧的对象进行调用。

让我们描述每种方法

IntStream.rangeClosed(1, 5).map(x -> 6-x)
                           .forEach(System.out::println);

这有点笨拙(希望很快会添加一个逐步版本),但它确实有效。如果我们经常需要步长,我们可以基于IntStream类创建自己的步长。

  • rangerangeClosed 方法生成一个流,该流具有一个有序的整数管道,从第一个数字开始,到第二个数字结束。区别在于rangeClosed包含终点,而我们的范围不包含。目前还没有带步长或递减值的版本——如果起始值超过最后一个元素,则管道将初始化为空。如果我们想要一个步长,我们使用map进行转换。
  • of 方法将一个或多个值放入管道中。多值版本接受可变数量的int(这意味着我们也可以传递一个int数组)。
  • 最后,concat 方法可以用于将两个或多个IntStream组合成单个IntStream

请注意,还有一个empty()方法,它会生成一个空流。

range 的一个用法是函数式for循环。它是函数式的,因为它没有可变的循环变量。上面的示例已经演示了这一点——循环的“主体”只是打印了循环计数器。

如果我们想要嵌套两个循环呢?这很容易

public class Multiplication
{
	public static void main(String[] args)
	{
		IntStream.rangeClosed(1, 10)
		         .forEach(i -> IntStream.rangeClosed(1, 10)
                         .forEach(
            j -> System.out.println(i + " * " + j + " = " + i * j)));
	}
}

通过使用forEach操作,我们可以将每个元素映射到另一个流。我们不必立即使用该元素,我们可以在管道的后面使用它。在这个例子中,我们使用两个流来生成乘法表。

我们还可以将IntStream的输出保存到数组中,如下所示

	int[] a = IntStream.rangeClosed(1, 10).toArray();

这为我们提供了一种非常方便的方法来将数组初始化为一系列整数。

管道操作toArray返回一个int[]。如果我们想从两个或多个嵌套循环创建数组呢?我们在嵌套版本中遇到的问题是,我们不能在内循环中使用toArray(),因为内循环是map函数的一部分,而该函数期望的是int而不是int[]。这意味着我们必须使用另一个技巧

	int[] a = IntStream.rangeClosed(1, 10)
			.flatMap(i -> IntStream.rangeClosed(1, 10)
                                          .map(j -> i * j))
            .toArray();

在这里,我们使用flatMapflatMap 将多个IntStream(这里有 10 个)展平为管道元素(int)。然后将其传递给下一个操作,即toArray()

传递给flatMap 的 lambda 表达式被转换为IntFunction,其apply函数接受一个int并返回一个IntStream。这是一个显式实现它的内部类

	private static class MultiplicationTable implements
			IntFunction<IntStream>
	{
		@Override
		public IntStream apply(int value)
		{
			return IntStream.rangeClosed(1, 10).map(j -> value * j);
		}
	}

使用调用

		int[] a = IntStream.rangeClosed(1, 10)
			               .flatMap(new MultiplicationTable())
			               .toArray();

最后,这是一个使用局部函数的版本

public class Multiplication
{
	private IntStream getTable(int i)
	{
		return IntStream.rangeClosed(1, 10).map(j -> i * j);
	}

	public void test()
	{
		int[] a = IntStream.rangeClosed(1, 10).flatMap
                                               (this::getTable)
					       .toArray();

		Arrays.stream(a).forEach(System.out::println);
	}

	public static void main(String[] args)
	{
		new Multiplication().test();
	}
}

请注意,我们还使用了Arrays辅助类上的stream函数来打印数组。为此,我们需要将数组传递到stream函数中,然后我们可以使用forEach进行打印。

这应该可以帮助你入门使用IntStream。请注意,还有LongDouble的专用流,你可能也需要看看。

修订历史

  • 2014年7月2日 -更正了类别
© . All rights reserved.