这个假期,几家在装修,很吵,说是不准节假日装修,谁理。我觉得中国房子的质量除了90年代的比较差外,很大程度都是装修给搞坏的,这家抡大锤,那家掀地板。就是个碉堡,也有毁坏的日子。
什么是流
对于Collection提供stream,也就是包括List(例如Vector,ArrayList,LinkedList),Set(如HashSet,LinkedHashSet,TreeSet)。流的处理一般如下:
Java 8中的流
在看EL如何支持流,我们先看看Java8中的Stream。
例子一:test0()和test1()是一样的,采用了Lambda表达方式写:
public void test0(){
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Stream<Integer> stream = numbers.stream();
stream.filter((x) -> {
return x % 2 == 0;
}).map((x) -> {
return x * x;
}).forEach(System.out::println);
}
public void test1(){
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Stream<Integer> stream = numbers.stream();
stream.filter(x -> x % 2 == 0 )
.map(x -> x * x)
.forEach(System.out::println);
}
例子二:提供一个元素类型比较复杂的例子
public class Book{
private String title;
private String author;
…… getter & setter……
public String toString(){
return "Title("+title+"):Author("+author+")";
}
}
private void testStream(){
Collection<Book> db = new Vector<>();
… 加入元素,从略 …
// 测试1:进行过滤(title中含有A的),然后将过滤后的打印出来
db.stream().filter(x->x.getTitle().contains("A"))
.forEach(System.out::println);
// 测试2:进行过滤(title中含有A的),然后将过滤后的提取某些部分(类型变成String)
// 对于stream进行intermediate操作时,会改变stream,但不会改变原来的数据,即不会改变db,因此我们再次进行db.stream()即可。
db.stream().filter(x->x. getTitle().contains("A"))
.map(x->x.getAuthor()) //相当于 (x)->{return x.getAuthor();}
.forEach(System.out::println);
// 测试3:进行过滤(title中含有A的),然后将过滤后进行重新处理map,将结果输出为Array
// 此处如果采用String[] authors,进行类型转换,会出现错误ClassCastException,应在元素时作转换
Object[] authors = (Object[]) db.stream().filter(x->x. getTitle().contains("A"))
.map(x->x.getAuthor())
.toArray();
for(Object obj :authors){
System.out.println((String)obj);
}
}
EL中的Stream
Intermediate操作
EL3.0支持Stream,但EL 3.0是运行在Java 7中的,因此采用了EL自己实现的Stream API。下面是一个例子:
books.stream().filter(b->b.title == 'Professional Java for Web Applications')
.map(b->{ 'title':b.title, 'author':b.author })
.toList();
filter()
流化后得到Stream<E>,E为元素类型,filter进行过滤,采用Lambda表达式,有一个预设的参数Predicate<E>,返回boolean的判断,只有返回为true的元素才被留在stream中。
distinct()
去除相同的元素。
${[1, 2, 3, 3, 4, 5, 5, 5, 5, 6].stream().distinct().toList()}
forEach()
对每个元素进行操控,不返回值。下面是一个java例子,打印每个元素的平方。
public void test3(){
List<Integer> numbers = Arrays.asList(1, 2, 3, 3, 5, 5, 5, 5, 5, 10);
Stream<Integer> stream = numbers.stream();
stream.distinct()
.forEach(x->System.out.println(x*x));
}
sorted()
排序。对于数字,可以直接使用sorted进行排序,对于一些复制的元素,可以如同 java.util.Comparator<E>进行排序,例如我们已经将request.setAttribute("books", books);在EL如下操作:
${[8, 3, 19, 5, 7, -2, 0].stream().sorted()} <br/>
${books.stream().sorted((b1,b2) -> b1.title.compareTo(b2.title)).toList()}
limit()和substream()
取部分元素,limit是前多少个,substream()是从多少到多少。numbers是[0, 1, 2, 3, 4, 5, 6],EL如下:
${numbers.stream().sorted().limit(3).toList()} <br/> <!-- 显示[0, 1, 2] -->
${numbers.stream().sorted().substream(2,5).toList()} <!-- 显示[2, 3, 4] -->
map()
map可以将Stream<A>转换为Stream<B>,例如
<!-- 1、map将Stream<Book>转换为Stream<String> -->
${books.stream().map(b -> b.title).toList()}
<!-- 2、map将Stream<Book>转换为Stream<List<Object>> -->
${books.stream().map(b -> [b.title, b.author]).toList()}
<!-- 3、map转为Stream<Map<Object,Object> -->
${books.stream().map(b -> {"title":b.title, "author":b.author}).toList()}
假设books中含有(Title A, AuthorA)和(Title B,Author B)两本书,则输出:
[Title A, Title B]
[[Title A, Author A], [Title B, Author B]]
[{author=Author A, title=Title A}, {author=Author B, title=Title B}]
我们还可以转成其他的类。
Terminal操作
返回Collection
可以使用toArray和toList返回,返回String[]和List<String>,不过在html中直接输出Object[]会显示一个对象地址,要显示,需要指定具体那个元素。List具有toString(),可以直接显示内容,在之前的例子可以看到。
${books.stream().map(b -> b.title).toArray()} 显示 [Ljava.lang.Object;@d8cd02
${books.stream().map(b -> b.title).toArray()[0]} 显示Title A
可以使用iterator获取一个java.util.Iterator。
使用Aggregate函数
count()可以获取元素的个数,sum()可以求和,可以直接输出结果。
average()要求元素是数,min()和max()要求元素可比较,它们返回都是org.apache.el.stream.Optional的对象Optional<E>,不能直接输出(给出的是地址),需要通过get()获取object的内容。Optional<E>表明可以为null。
${cartItems.stream().map(i -> i.price() * i.quantity()).sum()}
${books.stream().map(b->b.price).min().get()}
${numbers.stream().average().get()}
返回Frist Value
findFirst()返回Optional<E>,第一个元素。
例子
我们以使用前面User类,设置里面元素并request.setAttribute("users", users),具体从略。下面看看jsp的代码。先过滤只剩名字含有“1”的,然后按名字排序,重新组成新的流,返回List,利用List里面的System.out输出结果。
……
<body>
${users.stream().filter(u->fn:contains(u.username,'1'))
.sorted((u1,u2)->(x = u1.lastName.compareTo(u2.lastName);
x == 0 ? u1.firstName.compareTo(u2.firstName):x))
.map(u->{'username':u.username,'first':u.firstName,'last':u.lastName})
.toList()}<br/>
</body>
</html>