Java8 特性——增强集合处理 Stream 操作

版权声明:转载注明出处,谢谢合作。 https://blog.csdn.net/Mr_dsw/article/details/83183270

Stream 作为 Java8 中的一大亮点,专注于对集合(Collection)进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。Stream API 借助于同样新出现的 Lambda 表达式,极大的提高编程效率和程序可读性。同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用 fork/join 并行方式来拆分任务和加速处理过程。通常编写并行代码很难而且容易出错, 但使用 Stream API 无需编写一行多线程的代码,就可以很方便地写出高性能的并发程序。

1. Stream 基本介绍

Stream 代表数据流,流中的元素可能是有限个也可能是无限个。

Stream和其它集合类的区别在于:其它集合类主要关注与有限数量的数据的访问和有效管理(增删改),而Stream并没有提供访问和管理元素的方式,而是通过声明数据源的方式,利用可计算的操作在数据源上执行,当然BaseStream.iterator() 和 BaseStream.spliterator()操作提供了遍历元素的方法。

Stream 的类型有:IntStream、LongStream 和 DoubleStream。

2. Stream 的创建

常见的 Stream 创建方式有:

  • 通过 Stream 接口提供的静态工厂方法,Stream.of()、Stream.generate() 等;
  • 通过 Collection 接口的默认方法,stream()、parallelStream() 等;

当然还有其它方法,比如 Arrays.stream()、StreamSupport.stream() 。

2.1 通过 Stream 接口静态方法

Stream.of()

@SafeVarargs
@SuppressWarnings("varargs") // Creating a stream from an array is safe
public static<T> Stream<T> of(T... values) {
    return Arrays.stream(values);
}

内部通过 Arrays.stream() 进行创建。

示例:

Stream<String> streamStr = Stream.of("a","b","c");
Stream<Integer> stream = Stream.of(1,2,3,4,5);

Stream.generate()

public static<T> Stream<T> generate(Supplier<T> s) {
    Objects.requireNonNull(s);
    return StreamSupport.stream(
            new StreamSpliterators.InfiniteSupplyingSpliterator.OfRef<>(Long.MAX_VALUE, s), false);
}

内部通过 StreamSupport 进行生成。

示例:

Stream.generate(()->{
	return Math.random();
});

Stream.generate(new Supplier<Integer>() {

	@Override
	public Integer get() {
		return Math.round(5);
	}
});

2.2 通过 Collection 的默认方法

在 JDK1.8 中针对 Collection 接口进行了改动,其中包括转换 stream 的 default 方法。

/**
 * Returns a sequential {@code Stream} with this collection as its source.
 *
 * <p>This method should be overridden when the {@link #spliterator()}
 * method cannot return a spliterator that is {@code IMMUTABLE},
 * {@code CONCURRENT}, or <em>late-binding</em>. (See {@link #spliterator()}
 * for details.)
 *
 * @implSpec
 * The default implementation creates a sequential {@code Stream} from the
 * collection's {@code Spliterator}.
 *
 * @return a sequential {@code Stream} over the elements in this collection
 * @since 1.8
 */
default Stream<E> stream() {
    return StreamSupport.stream(spliterator(), false);
}

/**
 * Returns a possibly parallel {@code Stream} with this collection as its
 * source.  It is allowable for this method to return a sequential stream.
 *
 * <p>This method should be overridden when the {@link #spliterator()}
 * method cannot return a spliterator that is {@code IMMUTABLE},
 * {@code CONCURRENT}, or <em>late-binding</em>. (See {@link #spliterator()}
 * for details.)
 *
 * @implSpec
 * The default implementation creates a parallel {@code Stream} from the
 * collection's {@code Spliterator}.
 *
 * @return a possibly parallel {@code Stream} over the elements in this
 * collection
 * @since 1.8
 */
default Stream<E> parallelStream() {
    return StreamSupport.stream(spliterator(), true);
}

示例:

public static void main(String[] args) {
	List<Integer> list = new ArrayList<>();
	list.add(1);
	list.add(2);
	list.add(3);
	list.add(4);
	list.stream();
	list.parallelStream();
}

在实际的开发中,一般我们通过 Collection 的 default 方法来获取集合对应的 Stream,针对该 Stream 进行处理。

3. Stream 的转换

Stream 的转换主要是针对 Stream 提供的方法来进行针对 Stream 的处理,比如过滤、判断、计数等。

3.1 forEach(Consumer<? super T> action)

遍历元素,Consumer 接口是输入一个参数,无返回结果。

示例:

public static void main(String[] args) {
	List<Integer> list = new ArrayList<>();
	list.add(1);
	list.add(2);
	list.add(3);
	list.add(4);
	list.stream().forEach((x)->{
		System.out.println(x);
	});
}
//结果
1
2
3
4

这里的例子中都直接使用 lambda 表达式进行书写。

3.2 distinct()

去重,跟 SQL 中的关键字作用相同,经过 distinct 处理的 Stream 里面不包含重复元素。

示例:

public static void main(String[] args) {
	List<Integer> list = new ArrayList<>();
	list.add(1);
	list.add(2);
	list.add(3);
	list.add(4);
	list.add(1);
	list.add(3);
	list.stream().distinct().forEach(x ->{
		System.out.println(x);
	});
}
//结果
1
2
3
4

3.3 filter(Predicate<? super T> predicate)

针对 Stream 中的元素根据指定的 predicate 函数进行过滤,新生成的 Stream 只包含过来后的元素。Predicate 接口是输入一个参数,返回一个布尔类型。

示例:

public static void main(String[] args) {
	List<Integer> list = new ArrayList<>();
	list.add(1);
	list.add(2);
	list.add(3);
	list.add(4);
	list.add(1);
	list.add(3);
	list.stream().filter(x->{
		if (x > 2) {
			return true;
		}
		return false;
	}).forEach(x->{
		System.out.println(x);
	});;
}
//结果
3
4
3

3.4 map(Function<? super T, ? extends R> mapper)

对于 Stream 中包含的元素使用给定的转换函数进行转换操作,新生成的 Stream 只包含转换生成的元素。这个方法有三个对于原始类型的变种方法,分别是:mapToInt,mapToLong 和 mapToDouble。这三个方法也比较好理解,比如 mapToInt 就是把原始 Stream 转换成一个新的 Stream,这个新生成的 Stream 中的元素都是 int 类型。之所以会有这样三个变种方法,可以免除自动装箱/拆箱的额外消耗。

public static void main(String[] args) {
	List<Integer> list = new ArrayList<>();
	list.add(1);
	list.add(2);
	list.add(3);
	list.add(4);
	list.add(1);
	list.add(3);
	list.stream().map((x)->{
		if (x > 2) {
			return x * 2;
		}else {
			return x * 3;
		}
	}).forEach(x->{
		System.out.println(x);
	});;
}
//结果
3
6
6
8
3
6

3.5 flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)

将给定的元素转换成 Stream 流进行输出。

示例:

public static void main(String[] args) {
	List<Integer> list = new ArrayList<>();
	list.add(1);
	list.add(2);
	list.add(3);
	list.add(4);
	list.add(1);
	list.add(3);
	
	list.stream().flatMap(x ->{
		return Stream.of(x * 2);
	}).forEach(x->{
		System.out.println(x);
	});;
}

3.6 sorted()

进行排序。

示例:

public static void main(String[] args) {
	List<Integer> list = new ArrayList<>();
	list.add(1);
	list.add(2);
	list.add(3);
	list.add(4);
	list.add(1);
	list.add(3);
	
	list.stream().sorted().forEach(x->{
		System.out.println(x);
	});;
}

3.7 peek(Consumer<? super T> action)

生成一个包含原Stream的所有元素的新Stream,同时会提供一个消费函数(Consumer实例),新Stream每个元素被消费的时候都会执行给定的消费函数。

示例:

public static void main(String[] args) {
	List<Integer> list = new ArrayList<>();
	list.add(1);
	list.add(2);
	list.add(3);
	
	list.stream().peek(x->{
		x = x + 10;
		System.out.println(x+"--");
	}).forEach(x->{
		System.out.println(x);
	});
}
//结果
11--
1
12--
2
13--
3

3.8 limit(long maxSize)

对一个 Stream 进行截断操作,获取其前N个元素,如果原 Stream 中包含的元素个数小于 N,那就获取其所有的元素。

示例:

public static void main(String[] args) {
	List<Integer> list = new ArrayList<>();
	list.add(1);
	list.add(2);
	list.add(3);
	
	list.stream().limit(2).forEach(x->{
		System.out.println(x);
	});
}
//结果
1
2

3.9 skip(long n)

返回一个丢弃原 Stream 的前N个元素后剩下元素组成的新 Stream,如果原 Stream 中包含的元素个数小于 N,那么返回空 Stream。

示例:

public static void main(String[] args) {
	List<Integer> list = new ArrayList<>();
	list.add(1);
	list.add(2);
	list.add(3);
	
	list.stream().skip(1).forEach(x->{
		System.out.println(x);
	});
}
// 结果
2
3

3.10 reduce 操作

在 Stream 中提供了 reduce 操作,reduce 操作实现从 Stream 中元素组合起来,依照特定的某种规则进行计算。比如常见的:sum、min、max、average 计算操作。reduce 操作提供三个重载函数:

  • T reduce(T identity, BinaryOperator accumulator);
  • Optional reduce(BinaryOperator accumulator);
  • U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator combiner);

在这里引入了 BinaryOperator 接口,这个接口继承 BiFunction 接口。

@FunctionalInterface
public interface BiFunction<T, U, R> {

    R apply(T t, U u);

    default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t, U u) -> after.apply(apply(t, u));
    }
}

包含一个两个参数的 apply 方法。

sum 操作

public static void main(String[] args) {
    Stream<Integer> list = Stream.of(1,2,3,4,5,6);
    Optional<Integer> result = list.reduce((x, y) -> x + y);
    System.out.println(result.get());
}

max 操作

public static void main(String[] args) {
	BinaryOperator<Integer> binaryOperator = (Integer x, Integer y) -> {
		if (x < y) {
			return y;
		} else {
			return x;
		}
	};
	
	Stream<Integer> stream = Stream.of(1,2,3,4,5,6);
	System.out.println("max:" + stream.reduce(binaryOperator).get());
	
	Optional<Integer> optional = stream.reduce((Integer x, Integer y) -> {
		if (x < y) {
			return y;
		} else {
			return x;
		}
	});
	
	System.out.println("max:" + optional.get());
}

在上面的示例中,我们可以通过定义 BinaryOperation 来实现操作逻辑,也可以使用系统封装好的函数:Integer::max 进行处理。

min 操作

public static void main(String[] args) {
	BinaryOperator<Integer> binaryOperator = (Integer x, Integer y) -> {
		if (x < y) {
			return x;
		} else {
			return y;
		}
	};
	
	Stream<Integer> stream = Stream.of(1,2,3,4,5,6);
	System.out.println("min:" + stream.reduce(binaryOperator).get());
	
	Optional<Integer> optional = Stream.of(1,2,3,4,5,6).reduce((Integer x, Integer y) -> {
		if (x < y) {
			return x;
		} else {
			return y;
		}
	});
	
	System.out.println("min:" + optional.get());
}

总结

本节中介绍 Java8 中针对集合的增强操作,熟练的使用这些方法可以提升代码的简洁性。

猜你喜欢

转载自blog.csdn.net/Mr_dsw/article/details/83183270