Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。在Java 8中,得益于Lambda所带来的函数式编程,引入了一个全新的Stream概念,用于解决已有集合类库既有的弊端。
几乎所有的集合(如 Collection 接口或 Map 接口等)都支持直接或间接的遍历操作。而当我们需要对集合中的元 素进行操作的时候,除了必需的添加、删除、获取外,最典型的就是集合遍历。例如:
public static void listDemo() {
List<String> list = new ArrayList<> ();
list.add ("1");
list.add ("2");
list.add ("3");
list.add ("4");
list.add ("5");
for (String l : list) {
System.out.print (l + " "); // 1 2 3 4 5
}
}
Java 8 的Lambda 让我们可以更加专注于做什么(What),而不是怎么做(How),现在,我们仔细体会一下上例代码,可以发现:
为什么使用循环?因为要进行遍历。但循环是遍历的唯一方式吗?遍历是指每一个元素逐一进行处理,而并不是从第一个到最后一个顺次处理的循环。前者是目的,后者是方式。试想一下,如果希望对集合中的元素进行筛选过滤:
那怎么办?在Java 8之前的做法可能为:
public static void listDemo() {
List<String> list = new ArrayList<> ();
list.add ("1");
list.add ("12");
list.add ("3");
list.add ("14");
list.add ("5");
List<String> list1 = new ArrayList<> ();
for (String li : list) {
// 过滤条件1
if (li != null && li.startsWith ("1")) {
list1.add (li);
}
}
System.out.println (list1); // [1, 12, 14]
List<String> result = new ArrayList<> ();
for (String li : list1) {
// 过滤条件2
if (li != null && li.length () == 2) {
result.add (li);
}
}
for (String li : result) {
System.out.print (li + " "); // 12 14
}
}
使用Stream 处理同样的逻辑,代码如下,可以看出使用Stream 的代码更简洁,更优雅。
public static void listStreamDemo() {
List<String> list = new ArrayList<> ();
list.add ("1");
list.add ("12");
list.add ("3");
list.add ("14");
list.add ("5");
list.stream ()
.filter (s -> s.startsWith ("1"))
.filter (s -> s.length () == 2)
.forEach (System.out::println);
}
获取一个流非常简单,有以下几种常用的方式:
Map<String, String> map = new HashMap<> ();
Stream<String> keyStream = map.keySet ().stream ();
Stream<String> valueStream = map.values ().stream ();
Stream<Map.Entry<String, String>> entryStream = map.entrySet ().stream ();
int[] intArray = {1, 2, 3, 4, 5, 6, 7, 8, 9};
Stream<int[]> intStream = Stream.of (intArray);
Integer[] integerArray = {1, 2, 3, 4, 5, 6, 7, 8, 9};
Stream<Integer> integerStream = Stream.of (integerArray);
可以通过 filter 方法将一个流转换成另一个子集流。方法签名:
Stream<T> filter(Predicate<? super T> predicate);
该接口接收一个 Predicate 函数式接口参数(可以是一个Lambda或方法引用)作为筛选条件,该接口唯一一个抽象方法:
boolean test(T t);
private static void filterDemo() {
List<String> list = new ArrayList<> ();
list.add ("1");
list.add ("12");
list.add ("3");
list.add ("14");
list.add ("5");
System.out.println (list);
// list.stream ().filter (new Predicate () {
// @Override
// public boolean test(String s) {
// if (s.length () == 2) {
// return true;
// } else {
// return false;
// }
// }
// });
list.stream ().filter (s -> s.length () == 2);
}
// 省
public class Province {
private String pname; // 省名称
private List<City> cities;
public String getPname() {
return pname;
}
public void setPname(String pname) {
this.pname = pname;
}
public List<City> getCities() {
return cities;
}
public void setCities(List<City> cities) {
this.cities = cities;
}
@Override
public String toString() {
return "Province{" +
"pname='" + pname + '\'' +
", cities=" + cities +
'}';
}
}
// 市
public class City {
private String cid; // 城市id
private String cname; // 城市名称
public City(String cid, String cname) {
this.cid = cid;
this.cname = cname;
}
public String getCid() {
return cid;
}
public void setCid(String cid) {
this.cid = cid;
}
public String getCname() {
return cname;
}
public void setCname(String cname) {
this.cname = cname;
}
@Override
public String toString() {
return "City{" +
"cid='" + cid + '\'' +
", cname='" + cname + '\'' +
'}';
}
}
static List<City> cities = new ArrayList<> ();
static {
cities.add (new City ("12", "上海"));
cities.add (new City ("12", "上海"));
cities.add (new City ("12", "上海"));
cities.add (new City ("13", "南京"));
cities.add (new City ("14", "常州"));
}
private static void filterDistinctDemo() {
// cities.stream ().filter (distinctByKey(city -> city.getCname ())).forEach (city -> System.out.println (city));
// 下面这行代码等同于上面
cities.stream ().filter (distinctByKey (new Function<City, String> () {
// 回调函数
@Override
public String apply(City city) {
// 去重字段
return city.getCname ();
}
})).forEach (city -> System.out.println (city));
}
private static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
Map<Object,Boolean> seen = new ConcurrentHashMap<> ();
// return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
// 下面这段代码等同于上面这行
Predicate<T> predicate = new Predicate<T> () {
// filter:test 方法返回true时,保留;返回false时,过滤
@Override
public boolean test(T t) {
// keyExtractor.apply (t) 会回掉上面的apply 方法,回去返回值
Boolean b = seen.putIfAbsent (keyExtractor.apply (t), Boolean.TRUE);
// putIfAbsent 首次插入到map 的key,该方法返回null,不是首次时返回true
return b == null;
}
};
return predicate;
}
City{cid='12', cname='上海'}
City{cid='13', cname='南京'}
City{cid='14', cname='常州'}
如果需要将流中的元素映射到另一个流中,可以使用 map 方法。该接口需要一个 Function 函数式接口参数,可以将当前流中的T类型数据转换为另一种R类型的流。 方法签名:
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
public static void mapDemo() {
Stream<String> original = Stream.of("10", "12", "18");
Stream<Integer> result = original.map(Integer::parseInt);
}
public static void mapDemo2 () {
Stream<City> cityStream = entities.stream ().map (entity -> {
City city = new City (entity.getCid (), entity.getCname ());
return city;
});
}
limit 方法可以对流进行截取,只取用前n个。参数是一个long型,如果集合当前长度大于参数则进行截取;否则不进行操作。方法签名:
Stream<T> limit(long maxSize);
public static void limitDemo() {
Stream<String> original = Stream.of("10", "12", "18").limit (2);
original.forEach (o -> {
System.out.print (o + " ");
});
}
10 12
如果希望跳过前几个元素,可以使用 skip 方法获取一个截取之后的新流;如果流的当前长度大于n,则跳过前n个;否则将会得到一个长度为0的空流。方法签名:
Stream skip(long n);
public static void skipDemo() {
Stream<String> original = Stream.of("10", "12", "18").skip (2);
original.forEach (o -> {
System.out.print (o + " ");
});
}
18
正如旧集合 Collection 当中的 size 方法一样,流提供 count 方法来数一数其中的元素个数。该方法返回一个long值代表元素个数(不再像旧集合那样是int值)。方法签名:
long count();
public static void countDemo () {
long count = Stream.of ("10", "12", "18").skip (2).count ();
System.out.println (count);
}
1
如果有两个流,希望合并成为一个流,那么可以使用 Stream 接口的静态方法 concat 。方法签名:
public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
public static void contactDemo() {
Stream<String> a = Stream.of ("10", "12", "18");
Stream<Integer> b = Stream.of (1, 2, 3, 4);
Stream.concat (a, b).forEach (s -> {
System.out.print (s + " ");
});
}
10 12 18 1 2 3 4
forEach 与for循环中的“for-each”昵称不同,该方法并不保证元素的逐一消费动作在流中是被有序执行的。
void forEach(Consumer<? super T> action);
forEachOrdered 是按顺序处理的,没有forEach 的处理速度快
void forEachOrdered(Consumer<? super T> action);
对Stream 流这两个方法是一样的,因为stream() 是串行流,对parallelStream () 前者是无序的,后者是有序的
public static void forEachDemo() {
List<Integer> numbers = new ArrayList<> (Arrays.asList (1, 2, 3, 4, 5, 6, 7, 8, 9));
// 串行流 输出是有序的
numbers.stream ().forEach (n -> {
System.out.print (n + " ");
});
System.out.println ("\r\n-----------------");
// 输出是有序的
numbers.stream ().forEachOrdered (n -> {
System.out.print (n + " ");
});
System.out.println ("\r\n-----------------");
// 并行流 输出是无序的
numbers.parallelStream ().forEach (n -> {
System.out.print (n + " ");
});
System.out.println ("\r\n-----------------");
// 输出是有序的
numbers.parallelStream ().forEachOrdered (n -> {
System.out.print (n + " ");
});
}
1 2 3 4 5 6 7 8 9
-----------------
1 2 3 4 5 6 7 8 9
-----------------
6 5 7 9 8 2 3 1 4
-----------------
1 2 3 4 5 6 7 8 9
stream
转换为并发流,Stream
的父接口 java.util.stream.BaseStream
中定义了一个 parallel
方法:
S parallel();
只需要在流上调用一下无参数的 parallel 方法,那么当前流即可变身成为支持并发操作的流,返回值仍然为 Stream 类型。例如:
public static void parallelDemo() {
List<Integer> numbers = new ArrayList<> (Arrays.asList (1, 2, 3, 4, 5, 6, 7, 8, 9));
numbers.stream ().parallel ().forEach (n -> {
System.out.print (n + " ");
});
}
6 5 8 9 3 4 7 2 1
直接获取并发流,在通过集合获取流时,也可以直接调用接口 java.util.Collection
中的parallelStream 方法来直接获取支持并发操作的流。方法定义为:
default Stream<E> parallelStream() {
return StreamSupport.stream(spliterator(), true);
}
public static void parallelStreamDemo() {
List<Integer> numbers = new ArrayList<> (Arrays.asList (1, 2, 3, 4, 5, 6, 7, 8, 9));
numbers.parallelStream ().forEach (n -> {
System.out.print (n + " ");
});
}
6 5 7 9 8 2 3 1 4
该方法用于去除集合中重复的元素,方法签名为:
Stream<T> distinct();
基本类型或引用类型的String
可以直接使用该方法去重,如果是其他的引用类型的数据,需要重写hashCode
和 equals
方法,自定义去重规则。
public static void listDemo() {
List<String> list = new ArrayList<> ();
list.add ("1");
list.add ("1");
list.add ("1");
list.add ("12");
list.add ("3");
list.add ("14");
list.add ("5");
list.stream ().distinct ().forEach (s -> {
System.out.print (s + " ");
});
}
1 12 3 14 5
Stream
流提供 collect
方法,其参数需要一个 java.util.stream.Collector
接口对象来指定收集到哪种 集合中。幸运的是, java.util.stream.Collectors
类提供一些方法,可以作为 Collector
接口的实例:
public final class Collectors {
// 转换为 List 集合。
public static <T> Collector<T, ?, List<T>> toList();
// 转换为 Set 集合。
public static <T> Collector<T, ?, Set<T>> toSet();
// 转换为 Map 集合。
public static <T, K, U> Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper);
// 转换成集合,集合类型由参数类型确定
public static <T, C extends Collection<T>> Collector<T, ?, C> toCollection(Supplier<C> collectionFactory);
// 拼接字符串,有多个重载方法
public static Collector<CharSequence, ?, String> joining();
public static Collector<CharSequence, ?, String> joining(CharSequence delimiter);
public static Collector<CharSequence, ?, String> joining(CharSequence delimiter,
CharSequence prefix,
CharSequence suffix);
// 最大值、最小值、求和、平均值
public static <T> Collector<T, ?, Optional<T>> maxBy(Comparator<? super T> comparator);
public static <T> Collector<T, ?, Optional<T>> minBy(Comparator<? super T> comparator);
public static <T> Collector<T, ?, Integer> summingInt(ToIntFunction<? super T> mapper);
public static <T> Collector<T, ?, Double> averagingDouble(ToDoubleFunction<? super T> mapper);
// 分组:可以分成true和false两组,也可以根据字段分成多组
public static <T, K> Collector<T, ?, Map<K, List<T>>> groupingBy(Function<? super T, ? extends K> classifier);
public static <T, K, A, D> Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier,
Collector<? super T, A, D> downstream)
public static <T, K, D, A, M extends Map<K, D>> Collector<T, ?, M>
groupingBy(Function<? super T, ? extends K> classifier,
Supplier<M> mapFactory,
Collector<? super T, A, D> downstream)
// 只能分成true和false两组
public static <T> Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate);
// 映射
public static <T, U, A, R> Collector<T, ?, R> mapping(Function<? super T, ? extends U> mapper,
Collector<? super U, A, R> downstream);
//
public static <T, U> Collector<T, ?, U> reducing(U identity,
Function<? super T, ? extends U> mapper,
BinaryOperator<U> op);
}
public static void toListDemo() {
List<String> list = Stream.of ("10", "12", "18").collect (Collectors.toList ());
System.out.println (list); // [10, 10, 12, 18]
}
public static void toSetDemo() {
Set<String> set = Stream.of ("10", "10", "12", "18").collect (Collectors.toSet ());
System.out.println (set); // [12, 18, 10]
}
注意:不能有相同的key 否则会抛出java.lang.IllegalStateException: Duplicate key (重复的Key值)
public static void toMapDemo() {
Map<String, String> map = Stream.of ("10", "12", "18").collect (Collectors.toMap (key -> "key" + key, value -> value));
System.out.println (map); // {key12=12, key18=18, key10=10}
}
public static void toCollectionDemo() {
ArrayList<String> list = Stream.of ("10", "10", "12", "18").collect (Collectors.toCollection (ArrayList::new));
System.out.println (list); // [10, 10, 12, 18]
TreeSet<String> set = Stream.of ("10", "10", "12", "18").collect (Collectors.toCollection (TreeSet::new));
System.out.println (set); // [10, 12, 18]
}
public static void joiningDemo() {
String join = Stream.of ("10", "10", "12", "18").collect (Collectors.joining ());
System.out.println (join); // 10101218
join = Stream.of ("10", "10", "12", "18").collect (Collectors.joining (","));
System.out.println (join); // 10,10,12,18
join = Stream.of ("10", "10", "12", "18").collect (Collectors.joining (", ", "[", "]"));
System.out.println (join); // [10, 10, 12, 18]
}
public static void demo() {
Optional<String> optional = Stream.of ("10", "10", "12", "18").collect (Collectors.maxBy ((s1, s2) -> Integer.valueOf (s1) - Integer.valueOf (s2)));
System.out.println (optional.get ()); // 18
String max = Stream.of ("10", "10", "12", "18").collect (Collectors.collectingAndThen (Collectors.maxBy ((String s1, String s2) -> Integer.valueOf (s1) - Integer.valueOf (s2)), Optional::get));
System.out.println (max); // 18
String min = Stream.of ("10", "10", "12", "18").collect (Collectors.collectingAndThen (Collectors.minBy ((s1, s2) -> Integer.valueOf (s1) - Integer.valueOf (s2)), Optional::get));
System.out.println (min); // 10
Integer sum = Stream.of ("10", "10", "12", "18").collect (Collectors.summingInt (s -> Integer.valueOf (s.toString ())));
System.out.println (sum); // 50
Double average = Stream.of ("10", "10", "12", "18").collect (Collectors.averagingDouble (s -> Integer.valueOf (s.toString ())));
System.out.println (average); // 12.5
}
public static void groupByDemo() {
Map<Boolean, List<String>> partition = Stream.of ("10", "1", "2", "12", "18", "200").collect (Collectors.partitioningBy (s -> s.length () == 2));
System.out.println (partition); // {false=[1, 2, 200], true=[10, 12, 18]}
Map<Boolean, List<String>> partitioning = Stream.of ("10", "1", "2", "12", "18", "200").collect (Collectors.partitioningBy (s -> s.length () == 2, Collectors.toList ()));
System.out.println (partitioning); // {false=[1, 2, 200], true=[10, 12, 18]}
Map<Boolean, List<String>> grouping = Stream.of ("10", "1", "2", "12", "18", "200").collect (Collectors.groupingBy (s -> s.length () == 2));
System.out.println (grouping); // {false=[1, 2, 200], true=[10, 12, 18]}
/**
* entities:
* [
* ProvinceEntity{pname='上海', cid='11', cname='上海'},
* ProvinceEntity{pname='浙江', cid='12', cname='杭州'},
* ProvinceEntity{pname='浙江', cid='13', cname='台州'},
* ProvinceEntity{pname='浙江', cid='14', cname='宁波'},
* ProvinceEntity{pname='浙江', cid='15', cname='温州'},
* ProvinceEntity{pname='江苏', cid='16', cname='南京'},
* ProvinceEntity{pname='江苏', cid='17', cname='无锡'},
* ProvinceEntity{pname='江苏', cid='18', cname='盐城'},
* ProvinceEntity{pname='江苏', cid='19', cname='扬州'},
* ProvinceEntity{pname='江苏', cid='111', cname='宿迁'}
* ]
*/
Map<String, List<String>> provinces = entities.stream ().collect (Collectors.groupingBy (entity -> entity.getPname (), Collectors.mapping (entity -> entity.getCname (), Collectors.toList ())));
System.out.println (provinces); // {上海=[上海], 浙江=[杭州, 台州, 宁波, 温州], 江苏=[南京, 无锡, 盐城, 扬州, 宿迁]}
ConcurrentHashMap<Boolean, Integer> collect = Stream.of ("10", "1", "2", "12", "18", "200").collect (Collectors.groupingBy (s -> s.length () == 2, ConcurrentHashMap::new, Collectors.summingInt (s -> Integer.valueOf (s))));
System.out.println (collect); // {false=203, true=40}
}
public static void mappingDemo() {
/**
* [
* ProvinceEntity{pname='上海', cid='11', cname='上海'},
* ProvinceEntity{pname='浙江', cid='12', cname='杭州'},
* ProvinceEntity{pname='浙江', cid='13', cname='台州'},
* ProvinceEntity{pname='浙江', cid='14', cname='宁波'},
* ProvinceEntity{pname='浙江', cid='15', cname='温州'},
* ProvinceEntity{pname='江苏', cid='16', cname='南京'},
* ProvinceEntity{pname='江苏', cid='17', cname='无锡'},
* ProvinceEntity{pname='江苏', cid='18', cname='盐城'},
* ProvinceEntity{pname='江苏', cid='19', cname='扬州'},
* ProvinceEntity{pname='江苏', cid='111', cname='宿迁'}
* ]
*/
List<String> cities = entities.stream ().collect (Collectors.mapping (ProvinceEntity::getCname, Collectors.toList ()));
System.out.println (cities); // [上海, 杭州, 台州, 宁波, 温州, 南京, 无锡, 盐城, 扬州, 宿迁]
}
private static void reducingDemo() {
// sum: 是每次累计计算的结果,s是数组中的元素
Integer reducing = Stream.of ("10", "1", "2", "12", "18", "200").collect (Collectors.reducing (0, s -> Integer.valueOf (s), (sum, s) -> {
System.out.print("sum = " + sum + "; ");
System.out.println("s = " + s);
return sum + s;
}));
System.out.println ("reducing = " + reducing);
System.out.println ("----------------");
// 下面这段代码和上面的一样
int sum = 0;
List<String> integers = Arrays.asList("10", "1", "2", "12", "18", "200");
for (String item : integers) {
int s = Integer.valueOf (item);
System.out.print ("sum = " + sum + "; ");
System.out.println("s = " + s);
sum = sum + s;
}
System.out.println("sum = " + sum);
System.out.println ("----------------");
Integer result = Stream.of ("5", "1", "2", "4", "3", "6").collect (Collectors.reducing (1, x -> Integer.valueOf (x + 1), (multiply, x) -> {
System.out.print("x = " + x + "; ");
System.out.println ("multiply = " + multiply);
return multiply * x;
}));
System.out.println("result = " + result);
}
sum = 0; s = 10
sum = 10; s = 1
sum = 11; s = 2
sum = 13; s = 12
sum = 25; s = 18
sum = 43; s = 200
reducing = 243
----------------
sum = 0; s = 10
sum = 10; s = 1
sum = 11; s = 2
sum = 13; s = 12
sum = 25; s = 18
sum = 43; s = 200
sum = 243
----------------
x = 51; multiply = 1
x = 11; multiply = 51
x = 21; multiply = 561
x = 41; multiply = 11781
x = 31; multiply = 483021
x = 61; multiply = 14973651
result = 913392711
Stream提供 toArray 方法来将结果放到一个数组中,由于泛型擦除的原因,返回值类型是Object[]的:
Object[] toArray();
有了Lambda
和方法引用之后,可以使用 toArray 方法的另一种重载形式传递一个 IntFunction
的函数,继而从外面指定泛型参数。方法签名:
<A> A[] toArray(IntFunction<A[]> generator);
有了它,上例代码中不再局限于 Object[] 结果,而可以得到自定义的结果:
public static void toArrayDemo () {
Object[] objects = Stream.of ("10", "12", "18").toArray ();
String[] strings = Stream.of ("10", "12", "18").toArray (String[]::new);
}
private static class ProvinceEntity {
private String pname;
private String cid;
private String cname;
public ProvinceEntity(String pname, String cid, String cname) {
this.pname = pname;
this.cid = cid;
this.cname = cname;
}
public String getPname() {
return pname;
}
public void setPname(String pname) {
this.pname = pname;
}
public String getCid() {
return cid;
}
public void setCid(String cid) {
this.cid = cid;
}
public String getCname() {
return cname;
}
public void setCname(String cname) {
this.cname = cname;
}
}
static List<ProvinceEntity> entities = new ArrayList<> ();
static {
entities.add (new ProvinceEntity ("上海", "11", "上海"));
entities.add (new ProvinceEntity ("上海", "11", "上海"));
entities.add (new ProvinceEntity ("浙江", "12", "杭州"));
entities.add (new ProvinceEntity ("浙江", "13", "台州"));
entities.add (new ProvinceEntity ("浙江", "14", "宁波"));
entities.add (new ProvinceEntity ("浙江", "15", "温州"));
entities.add (new ProvinceEntity ("江苏", "16", "南京"));
entities.add (new ProvinceEntity ("江苏", "17", "无锡"));
entities.add (new ProvinceEntity ("江苏", "18", "盐城"));
entities.add (new ProvinceEntity ("江苏", "19", "扬州"));
entities.add (new ProvinceEntity ("江苏", "111", "宿迁"));
}
private static void steamDemo() {
//
Map<String, List<City>> collect = entities.stream ().collect (Collectors.groupingBy (entity -> {
String pname = entity.getPname ();
if (pname == null) {
pname = "";
}
return pname;
}, Collectors.mapping (entity -> {
City city = new City (entity.getCid (), entity.getCname ());
return city;
}, Collectors.toList ())));
System.out.println (collect);
}
{
上海=[City{cid='11', cname='上海'}],
浙江=[City{cid='12', cname='杭州'}, City{cid='13', cname='台州'}, City{cid='14', cname='宁波'}, City{cid='15', cname='温州'}],
江苏=[City{cid='16', cname='南京'}, City{cid='17', cname='无锡'}, City{cid='18', cname='盐城'}, City{cid='19', cname='扬州'}, City{cid='111', cname='宿迁'}]
}