一. Collections.unmodifiableList方法的使用与场景
参考文章:http://blog.csdn.net/cilen/article/details/7744969
在公司接触到Collections.unmodifiableList(List<? extends T> list))
觉得用法挺特殊的,所以学习了下,简单而言,看名字就知道,将参数中的List返回一个不可修改的List.
在《重构——改善既有代码的设计》一书中,有一种重构手法叫Encapsulate Collection
(封装集群),为了演示该重构手法,我写了四个类,通过对比重构前后的代码,加深对
这一重构手法的理解。
类Student有一ArrayList属性,如果没有阅读《重构——改善既有代码的设计》一书,
很多人可能会像我一样,如下设计类Student。但是,如果通过Student.getCourses()
获得对ArrayList属性引用后,就可以任意为Student对象添加“课程”,而Student对象
对此一无所知,这不符合面向对象编程的习惯。
package com.readonlylist;
import java.util.ArrayList;
public class Student
{
private String name;
private ArrayList<String> courses;
public Student(String name, ArrayList<String> courses)
{
this.name = name;
this.courses = courses;
}
public ArrayList<String> getCourses()
{
return courses;
}
public void setCourses(ArrayList<String> courses)
{
this.courses = courses;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
}
package com.readonlylist;
import java.util.ArrayList;
public class Test
{
public static void main(String[] args)
{
ArrayList<String> list = new ArrayList<String>();
list.add("001");
list.add("002");
Student s = new Student("Tom", list);
ArrayList<String> anotherList = s.getCourses();
anotherList.add("999");
System.out.println("Tom's course.length = " + s.getCourses().size());
}
}
重构后的Student类如下所示:
package com.readonlylist;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Student1
{
private String name;
private ArrayList<String> courses;
public Student1(String name, ArrayList<String> courses)
{
this.name = name;
this.courses = courses;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public void addCourse(String course)
{
courses.add(course);
}
public String removeCourse(String course)
{
boolean removed = courses.remove(courses);
if (removed)
{
return course;
}
else
{
return null;
}
}
public List<String> getCourses()
{
return Collections.unmodifiableList(courses);
}
}
package com.readonlylist;
import java.util.List;
import java.util.ArrayList;
public class Test1
{
public static void main(String[] args)
{
ArrayList<String> list = new ArrayList<String>();
list.add("001");
list.add("002");
Student1 s = new Student1("Tom", list);
List<String> anotherList = s.getCourses();
/**
* throws java.lang.UnsupportedOperationException
* should replace with s.addCourse(String course)
*/
anotherList.add("999");
// never reached
System.out.println("Tom's course.length = " + s.getCourses().size());
}
}
重构后,Student1类,仅对外提供的getCourses()方法,而没有setCourses()方法,而且
通过getCourses()方法获得的courses是“只读的”,如果你试图向其添加一个新课程,则
抛出java.lang.UnsupportedOperationException。你必须通过Student1.addCourse()来
向特定的Student1对象添加一个新课程。就好像,你必须让顾客自己向购物车里放食物,
而不能在顾客毫不知情下,偷偷向其购物车里放食物。
真是万物皆通情理啊:)
二. java8新特性(四):Stream流的使用
参考文章:http://blog.csdn.net/sunjin9418/article/details/53086565
过滤出素食
List<Dish> vegetarian = menu.stream().filter(Dish::isVegetarian).collect(Collectors.toList());
过滤出偶数,并且不重复的元素。
List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
numbers.stream()
.filter(i -> i % 2 == 0)
.distinct()
.forEach(System.out::println);
截断流
List<Dish> dishes = menu.stream()
.filter(d -> d.getCalories() > 300)
.limit(3)
.collect(toList());
跳过元素
List<Dish> dishes = menu.stream()
.filter(d -> d.getCalories() > 300)
.skip(2)
.collect(toList());
映射
List<String> dishNames = menu.stream()
.map(Dish::getName)
.collect(toList());
List<String> words = Arrays.asList("Java8", "Lambdas", "In", "Action");
List<Integer> wordLengths = words.stream()
.map(String::length)
.collect(toList());
展开流
List<String> uniqueCharacters = words.stream().map(w -> w.split("")).flatMap(Arrays::stream).distinct().collect(Collectors.toList());
查找和匹配
任意匹配
if(menu.stream().anyMatch(Dish::isVegetarian)){
System.out.println("The menu is (somewhat) vegetarian friendly!!");
}
全部匹配
boolean isHealthy = menu.stream().allMatch(d -> d.getCalories() < 1000);
全部不匹配
boolean isHealthy = menu.stream().noneMatch(d -> d.getCalories() >= 1000);
获取任意一个元素
Optional<Dish> dish =menu.stream() .filter(Dish::isVegetarian) .findAny();
归约
将一个流中的元素反复结合运算得到一个值。
使用循环求和
List<Integer> numbers = new ArrayList<>();
int sum1 = 0;
for(int x: numbers){
sum1 += x;
}
使用流的API求和
int sum2 = numbers.stream().reduce(0, (a,b) -> a + b);
求最大值
Optional<Integer> max = numbers.stream().reduce(Integer::max);
求最小值
Optional<Integer> min = numbers.stream().reduce(Integer::min);
使用reduce相对于使用逐步迭代的好处在于,外部迭代改成了内部迭代,在需要实现并行执行的操时作变得简单。
有状态操作和无状态操作
比如map或者filter会从输入流中获取每一个元素,并且在输出流中得到一个结果,这些操作没有内部状态,称为无状态操作。
但是像reduce、sum、max这些操作都需要内部状态来累计计算结果,所以称为有状态操作。
还有一些操作sort、distinct,看上去和filter、map差不多,他们接收一个流,再生成一个流,但是区别在于排序和去重复项需要知道先前的历史。比如排序就需要将所有元素放入缓存区后才能给输出流加入一个项目,这个操作对缓存的要求是无上限的,流有多大就需要多大的缓存才能进行运算。这些操作也是有状态操作。
中间操作和终端操作
中间操作就像是水管的一部分,终端操作就像水龙头,增加水管长度不会消耗水,只有打开水龙头才会开始消耗水。