1. 前言
这里也是从那几篇旧博客中抽出来的,不算一大部分了,主要介绍Optional、Stream、lambda、Buffer等相关知识点
2. Optional
基础
为了防止NullPointerException的一个工具类,Optional是一个容器,其值可以是null或者不是null。
通过对null进行封装,实现了函数式编程,不必写过多的if else进行判断。
其主要的函数包括:
- of:将非空的Bean转换成Optional
- ofNullable:将可以为空的Bean转换成Optional
- map:map是对Optional进行操作,这个map与mapReduce的map不同
- filter:对Optional进行操作,若返回为false则后边为null
- orElse:Optional转换成Bean,若为null返回指定值
- orElseThrow:Option转换成Bean,若为null抛异常
示例
示例1:
public static String getName(User u) {
if (u == null || u.name == null)
return "Unknown";
return u.name;
}
改写为Optional风格:
public static String getName(User u) {
return Optional.ofNullable(u)
.map(user->user.name)
.orElse("Unknown");
}
示例2:
public static String getChampionName(Competition comp) throws IllegalArgumentException {
if (comp != null) {
CompResult result = comp.getResult();
if (result != null) {
User champion = result.getChampion();
if (champion != null) {
return champion.getName();
}
}
}
throw new IllegalArgumentException("The value of param comp isn't available.");
}
改写成Optional风格:
public static String getChampionName(Competition comp) throws IllegalArgumentException {
return Optional.ofNullable(comp)
.map(Competition::getResult) // 相当于c -> c.getResult(),下同
.map(CompResult::getChampion)
.map(User::getName)
.orElseThrow(()->new IllegalArgumentException("The value of param comp isn't available."));
}
示例3:
对name进行验证的,通过filter传入验证函数
public void setName(String name) throws IllegalArgumentException {
this.name = Optional.ofNullable(name)
.filter(User::isNameValid)
.orElseThrow(()->new IllegalArgumentException("Invalid username."));
}
3. Stream
基础
表示能应用在一组元素执行的操作序列。Stream 操作分为中间操作和最终操作两种,最终操作是返回特定类型的计算结果,而中间操作返回 Stream 本身,这样你就可以将多个操作依次串起来。Stream 的创建需要指定一个数据源,比如 java.util.Collection
的子类,如 List 或者 Set,但不支持 Map。
示例
List<String> stringList = new ArrayList<>();
stringList.add("ddd2");
stringList.add("aaa2");
stringList.add("bbb1");
stringList.add("aaa1");
stringList.add("bbb3");
stringList.add("ccc");
stringList.add("bbb2");
stringList.add("ddd1");
// 测试 Filter(过滤)
stringList.stream().filter((s) -> s.startsWith("a")).forEach(System.out::println); // aaa2 aaa1
// 测试 Sort (排序)
stringList.stream().sorted().filter((s) -> s.startsWith("a")).forEach(System.out::println);// aaa1 aaa2
// 测试 Map 操作
stringList.stream().map(String::toUpperCase).sorted((a, b) -> b.compareTo(a)).forEach(System.out::println); // "DDD2", "DDD1", "CCC", "BBB3", "BBB2", "AAA2", "AAA1"
// 测试 Match (匹配)操作
boolean anyStartsWithA = stringList.stream().anyMatch((s) -> s.startsWith("a"));
System.out.println(anyStartsWithA); // true
boolean allStartsWithA = stringList.stream().allMatch((s) -> s.startsWith("a"));
System.out.println(allStartsWithA); // false
boolean noneStartsWithZ = stringList.stream().noneMatch((s) -> s.startsWith("z"));
System.out.println(noneStartsWithZ); // true
// 测试 Count (计数)操作
long startsWithB = stringList.stream().filter((s) -> s.startsWith("b")).count();
System.out.println(startsWithB); // 3
// 测试 Reduce (规约)操作
Optional<String> reduced = stringList.stream().sorted().reduce((s1, s2) -> s1 + "#" + s2);
reduced.ifPresent(System.out::println); // aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2
// 求和,sumValue = 10, 有起始值,返回的是int
int sumValue = Stream.of(1, 2, 3, 4).reduce(0, Integer::sum);
// 求和,sumValue = 10, 无起始值,返回的是Opitonal
int sumValue = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get();
4. lambda
lambda表达式是在js、python等语言中早就存在,Java在1.8版本中引入。这里简单做个介绍:
lambda之前的问题
String[] array = new String[] { "Apple", "Orange", "Banana", "Lemon" };
Arrays.sort(array, new Comparator<String>() {
public int compare(String s1, String s2) {
return s1.compareTo(s2);
}
});
在lambda出现之前,要想传入一个函数,需要采用类的方式来传入,由sort去调用固定的compare接口。
这种方式也能解决问题,但比较笨拙
这种方法有很多:如Runable
、Callable
、Consumer
等等
lambda方案
lambda的引入,简化了这个问题
String[] array = new String[] { "Apple", "Orange", "Banana", "Lemon" };
Arrays.sort(array, (s1, s2) -> {
return s1.compareTo(s2);
});
lambda如何做的
Comparator接口中只用一个抽象方法(非static、非default):compare,这种方法在Java1.8中被定义为单方法接口或者叫函数式接口,可以用注解@FunctionalInterface
标记。
例如:
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
每个 Lambda 表达式都能隐式地赋值给函数式接口,前提是lambda表达式的入参与返回类型要函数式接口匹配。
public interface MyInterface {
void printIt(String text);
}
MyInterface myInterface = (String text) -> {
System.out.print(text);
};
下边看一个对比的示例:
//定义一个函数式接口
@FunctionalInterface
public interface WorkerInterface {
public void doSomeWork();
}
public class WorkerInterfaceTest {
public static void execute(WorkerInterface worker) {
worker.doSomeWork();
}
public static void main(String [] args) {
//invoke doSomeWork using Annonymous class
execute(new WorkerInterface() {
@Override
public void doSomeWork() {
System.out.println("Worker invoked using Anonymous class");
}
});
//invoke doSomeWork using Lambda expression
execute( () -> System.out.println("Worker invoked using Lambda expression") );
}
}
5. Buffer与I/O流
IO流的、JavaIO流
buffer:内存中一块确定的临时存储区域。
stream:一段不确定长度的数据序列,可以认为stream就是I/O中input部分的FIFO实现。通过buffer可以增强stream的读写效率。
streams是读取、写入到文件、网络、设备中IO交互的抽象。对于字符流,抽象出了Reader与Writer,对于字节流抽象出了InputStream与OutputStream,本质上它们都是流。
以从硬盘上读取数据为例,一个字节一个字节的读取,效率太低,通过使用Buffer来加速读取,一次读取很多数据,然后存储到Buffer中。再从Buffer中一个字节一个字节的读取。