image距离上一篇的文章发布已经过了大半年,由于各种原因没有保持及时的更新,实在是惭愧,这里一节将继续讲解JAVA Stream的使用,合理使用Steam可以提升我们的开发效率同时还能简化代码。
Stream默认提供了很多的方法,基本上可以满足我们平时的使用,如果有特殊需要也可以自己扩展。
1.字符串连接
1.在没有使用Stream时,我们通常拼接字符是这样的
List<String> values = Arrays.asList("Tom", "Jack", "David", "Rose");
StringBuilder builder = new StringBuilder();
for (String s : values) {
builder.append(s);
}
这时候看起来还没有什么,如果我需要在每个字符串中间加一个分割符,什么之类,然后就是这样了
StringBuilder builder = new StringBuilder();
for (String s : values) {
builder.append(s).append("-");
}
// 替换1
String result = builder.substring(0, builder.length() - 1);
// 替换2
builder.delete(builder.length() - 1, builder.length());
out:
Tom-Jack-David-Rose
这时候如果再进行一些复杂的操作时,可能就不是怎么简单了,我再看看使用Stream的代码
List<String> values = Arrays.asList("Tom", "Jack", "David", "Rose");
// 一行代码就可以实现
String simple = values.stream().collect(Collectors.joining("-"));
// 如果我们需要在开始或者结尾加一点东西的时候就是这样
String complex = values.stream().collect(Collectors.joining("-", "[", "]"));
out:
Tom-Jack-David-Rose
[Tom,Jack,David,Rose]
经过源码分析后,joining() 方法的实现也是利用StringBuilder来实现的,只不过是JAVA提供的语法糖,以下提供部分源码
public static Collector<CharSequence, ?, String> joining(CharSequence delimiter) {
return joining(delimiter, "", "");
}
public static Collector<CharSequence, ?, String> joining(CharSequence delimiter,
CharSequence prefix,
CharSequence suffix) {
return new CollectorImpl<>(
() -> new StringJoiner(delimiter, prefix, suffix),
StringJoiner::add, StringJoiner::merge, // 这里的add就是使用StringBuilder中的append来实现的
StringJoiner::toString, CH_NOID);
}
public StringJoiner add(CharSequence newElement) {
prepareBuilder().append(newElement);
return this;
}
private StringBuilder prepareBuilder() {
if (value != null) {
value.append(delimiter);
} else {
value = new StringBuilder().append(prefix);
}
return value;
}
2.数据分组
现在的需求如下,从数据库中查找到一个班级的学生(List集合),需要按照性别分组,然后放入Map集合返回(结构:Map<String, List<Student>>)
- 在没有使用Stream之前,我们通常的写法
StudentService studentService = new StudentService();
List<Student> students = studentService.getList();
Map<String, List<Student>> groupStudent = new HashMap<>(5);
List<Student> boy = new ArrayList<>(16);
List<Student> girl = new ArrayList<>(16);
for (Student s : students) {
if (s.isMan()) {
boy.add(s);
continue;
}
girl.add(s);
}
groupStudent.put("body", boy);
groupStudent.put("girl", girl);
out:
{body=[Student{name='小明', age=18, gender='男', className='高三一班', score=null, classFee=50.0}, Student{name='小军', age=16, gender='男', className='高三二班', score=null, classFee=50.0}], girl=[Student{name='小丽', age=17, gender='女', className='高三一班', score=null, classFee=50.0}, Student{name='小丽', age=18, gender='女', className='高三二班', score=null, classFee=100.0}]}
- 使用Stream的处理方法
Map<String, List<Student>> result = students.stream()
.collect(Collectors.groupingBy(Student::getGender, Collectors.toList()));
out:
{女=[Student{name='小丽', age=17, gender='女', className='高三一班', score=null, classFee=50.0}, Student{name='小丽', age=18, gender='女', className='高三二班', score=null, classFee=100.0}], 男=[Student{name='小明', age=18, gender='男', className='高三一班', score=null, classFee=50.0}, Student{name='小军', age=16, gender='男', className='高三二班', score=null, classFee=50.0}]}
-
如果这时候我们再加一个需求就是先按照年级分组,然后在按照性别分组(结构:Map<String, Map<String, List<Student>>>),这时候,对于这种需求你是不是就觉得有些不爽了。这里我就不展示传统的写法了,相信你已经想到了
-
使用Stream实现
Map<String, Map<String, List<Student>>> advanced = students.stream()
.collect(Collectors.groupingBy(Student::getClassName,
Collectors.groupingBy(Student::getGender, Collectors.toList())));
out:
{高三二班={女=[Student{name='小丽', age=18, gender='女', className='高三二班', score=null, classFee=100.0}], 男=[Student{name='小军', age=16, gender='男', className='高三二班', score=null, classFee=50.0}]}, 高三一班={女=[Student{name='小丽', age=17, gender='女', className='高三一班', score=null, classFee=50.0}], 男=[Student{name='小明', age=18, gender='男', className='高三一班', score=null, classFee=50.0}]}}
网友评论