大纲
函数式编程学习的重点
Lambda表达式
- 基于匿名类
- 基于函数式接口
- JDK8自带的函数式接口
- 方法引用和构造器引用
Java Stream API
- 操作Collections
- 操作NIO2.0
- 线程解析
lambda表达式
实现的接口
public interface PriceDashboard {
Price getPrice(String symbol);
}
一般内部类
内部类可以调用外部属性
@Slf4j
public class DemoPart1Test {
private Map<String, Price> stockPrices = new HashMap<>();
private int accessCount = 0;
public void init() {
stockPrices.put("APPL", Price.builder()
.currPrice(new BigDecimal("130.06"))
.build());
}
private class PriceDashboardNewImpl implements PriceDashboard {
@Override
public Price getPrice(String symbol) {
log.info("log this inner class action");
return stockPrices.get(symbol);
// log.info("about to reference by this");
// return DemoPartOneTest.this.stockPrices.get(symbol);
}
}
public static void main(String[] args) {
DemoPart1Test test = new DemoPart1Test();
test.init();
PriceDashboard priceDashboard = test.new PriceDashboardNewImpl();
log.info(priceDashboard.getPrice("APPL").toString());
}
}
局部内部类
定义在成员方法中,在代码块之外无法使用
@Slf4j
public class DemoPart2Test {
private Map<String, Price> stockPrices = new HashMap<>();
private int accessCount = 0;
public void init() {
stockPrices.put("APPL", Price.builder()
.currPrice(new BigDecimal("130.06"))
.build());
}
public PriceDashboard priceBoard() {
class PriceDashboardInMethodImpl implements PriceDashboard {
@Override
public Price getPrice(String symbol) {
log.info("log this in-method action");
return stockPrices.get(symbol);
}
}
return new PriceDashboardInMethodImpl();
}
public static void main(String[] args) {
DemoPart2Test test = new DemoPart2Test();
test.init();
PriceDashboard priceDashboard = test.priceBoard();
log.info(priceDashboard.getPrice("APPL").toString());
}
}
匿名内部类
一般写法
函数式写法
public PriceDashboard anonPriceBoard() {
return symbol -> {
log.info("log this in-method anonymous action");
return stockPrices.get(symbol);
};
}
基于函数式接口的lambda表达式
函数式接口的使用只能存在一个抽象方法,存在多余的抽象方法则编译不通过。
@FunctionalInterface
public interface PriceCalculation {
BigDecimal increase(BigDecimal curr, BigDecimal inc);
static void staticMethod() {
System.out.println("static method");
}
default void defaultMethod() {
System.out.println("default method");
}
}
静态方法的调用可以通过接口名称直接调用,而default方法只能通过实现类来调用。
public class PriceCalculationImpl implements PriceCalculation{
@Override
public BigDecimal increase(BigDecimal curr, BigDecimal inc) {
return curr.add(inc);
}
@Override
public void defaultMethod() {
System.out.println("customized default");
}
public static void main(String[] args) {
PriceCalculation.staticMethod();
new PriceCalculationImpl().defaultMethod();
}
}
举例:
@FunctionalInterface
public interface Demo0ParamInterf {
String print();
}
@FunctionalInterface
public interface Demo1ParamInterf {
void print(int a);
}
@FunctionalInterface
public interface Demo2ParamInterf {
int print(int a, int b);
}
@FunctionalInterface
public interface Demo3ParamInterf {
int print();
boolean equals(Object a);
}
表达式
public class DemoParamTest {
public static void main(String[] args) {
Demo0ParamInterf impl0 = () -> "0 param";
Demo1ParamInterf impl1 = (a) -> System.out.println(a);
Demo2ParamInterf impl2 = (a, b) -> {
int c = a * b;
int d = Math.floorMod(c, a - 1);
return d;
};
}
}
JDK8中自带的函数式接口
Predicate判断
接口
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
default Predicate<T> negate() {
return (t) -> !test(t);
}
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
@SuppressWarnings("unchecked")
static <T> Predicate<T> not(Predicate<? super T> target) {
Objects.requireNonNull(target);
return (Predicate<T>)target.negate();
}
}
使用
@Slf4j
public class DemoPredicateTest {
@Test
public void SimplePredicate() {
Predicate<Integer> isAdult = age -> (age > 18);
log.info(String.valueOf(isAdult.test(20)));
log.info(String.valueOf(isAdult.test(16)));
}
@Test
public void AndPredicate() {
Predicate<Integer> isAdult = new Predicate<Integer>() {
@Override
public boolean test(Integer age) {
log.info("we are young once");
return age > 18;
}
};
Predicate<Integer> isRetired = age -> {
log.info("yes, pension");
return age > 70;
};
log.info(String.valueOf(isAdult.and(isRetired).test(25)));
}
@Test
public void OrPredicate() {
Predicate<Integer> cannotRead = age -> (age < 4);
Predicate<Integer> cannotCode = age -> (age > 99);
log.info("Should quit coding at 35? {}",
cannotRead.or(cannotCode).test(35));
}
@Test
public void NegatePredicate(){
Predicate<Integer> isAdult = age -> (age >18);
log.info(String.valueOf(isAdult.negate().test(16)));
}
@Test
public void compositePredicate(){
Predicate<Integer> cannotRead = age -> (age < 4);
Predicate<Integer> cannotCode = age -> (age > 99);
Predicate<Integer> compositePredicate = cannotRead
.or(cannotCode)
.negate();
log.info("Should quit coding at 35? {}", compositePredicate.test(35));
}
}
Consumer消费
接口
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
使用
@Slf4j
public class DemoConsumerTest {
@Test
public void LogConsumer() {
Consumer<Object> logConsumer = o -> {
log.info(o.toString());
};
logConsumer.accept("Print something");
logConsumer.accept(System.currentTimeMillis());
}
@Test
public void AndThen() {
Consumer<List<String>> upperCaseConsumer = strings -> {
for (int i = 0; i < strings.size(); i++) {
strings.set(i, strings.get(i).toUpperCase());
}
};
Consumer<List<String>> logConsumer = strings -> strings.forEach(str -> System.out.println(str));
List<String> list = Lists.newArrayList("foo", "bar", "baz");
upperCaseConsumer.andThen(logConsumer).accept(list);
}
}
Supplier供应商
接口
@FunctionalInterface
public interface Supplier<T> {
T get();
}
使用
@Slf4j
public class DemoSupplierTest {
@Test
public void RandomSupplier() {
Supplier<Double> doubleSupplier = Math::random;
Consumer<Double> logConsumer = num -> log.info(String.valueOf(num));
logConsumer.accept(doubleSupplier.get());
}
}
Function方法
接口
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
static <T> Function<T, T> identity() {
return t -> t;
}
}
使用
@Slf4j
public class DemoFunctionTest {
@Test
public void ArithmeticFunction() {
Function<Integer, Integer> doubled = x -> x * 2;
System.out.println(doubled.apply(3));
Function<Integer, Integer> halved = x -> x / 2;
System.out.println(halved.apply(10));
Function<Integer, Integer> inched = x -> x + 2;
System.out.println(inched.apply(20));
}
@Test
public void TypeTransformationFunction() {
Function<String, Integer> lengthFunction = str -> str.length();
System.out.println(lengthFunction.apply("woyouhuile"));
}
@Test
public void CompositeFunction() {
Function<Integer, Integer> doubled = x -> x * 2;
Function<Integer, Integer> inched = x -> x + 2;
System.out.println(doubled.compose(inched).apply(10));
System.out.println(doubled.andThen(inched).apply(10));
}
@Test
public void IdentityFunction() {
List<Stock> stocks = Lists.newArrayList(
Stock.builder().name("APPLE").symbol("APPL").build(),
Stock.builder().name("Amazon").symbol("AMZN").build(),
Stock.builder().name("Starbucks").symbol("SBUX").build()
);
Map<String, Stock> map = stocks.stream()
.collect(Collectors.toMap(Stock::getSymbol, Function.identity()));
System.out.println(map);
}
}
其他接口
在java.util 包中存在其他函数式接口
bitfunction就是接收两个参数返回一个参数的接口
因为通用的函数式接口存在包装类的装箱与拆箱的操作,因此定义了许多不同的函数式接口进行使用,在数据量大的时候减少消耗。
方法引用和构造器引用
静态方法引用
使用类名::方法名 的方式引用
public class DemoStaticMethodReferenceTest {
@Test
public void printStringTest() {
Thread t = new Thread(() -> printString());
Thread t1 = new Thread(DemoStaticMethodReferenceTest::printString);
t.start();
}
public static void printString() {
System.out.println("string");
}
}
非静态方法引用
定义一个比较器
public class StockComparator implements Comparator<Stock> {
@Override
public int compare(Stock o1, Stock o2) {
return o1.getSymbol().compareTo(o2.getSymbol());
}
}
使用@Builder 注解,可以使用build的方式创建类
@Data
@Builder
public class Stock {
public Stock() {
}
public Stock(String symbol, String name) {
this.symbol = symbol;
this.name = name;
}
private String symbol;
private String name;
}
使用
public class DemoInstanceMethodReferenceTest {
@Test
public void orderStockTest() {
List<Stock> stocks = Lists.newArrayList(
Stock.builder().name("APPLE").symbol("APPL").build(),
Stock.builder().name("Amazon").symbol("AMZN").build(),
Stock.builder().name("Starbucks").symbol("SBUX").build()
);
StockComparator stockComparator = new StockComparator();
stocks.stream()
.sorted((o1, o2) -> o1.getSymbol().compareTo(o2.getSymbol()))
.sorted(stockComparator::compare)
.collect(Collectors.toList());
stocks.stream()
.forEach(System.out::println);
}
}
构造器引用
接口
@FunctionalInterface
public interface StockCreator {
Stock getStock();
}
@FunctionalInterface
public interface StockCreator2 {
Stock getStock(String symbol, String name);
}
根据参数的类型以及数量,引用对应的构造器。不对应时,无法使用构造器引用。
public class DemoConstructorReferenceTest {
@Test
public void stockConstructorTest() {
StockCreator creator1 = Stock::new;
StockCreator2 creator2 = Stock::new;
Supplier<Stock> creator3 = Stock::new;
Stock stock1 = creator1.getStock();
Stock stock2 = creator2.getStock("APPL", "APPLE Inc");
Stock stock3 = creator3.get();
}
}
lambda表达式中的异常处理
需要抛出异常的方法
public class ComplexComputer {
public String encrypt(String input) throws GeneralSecurityException, InterruptedException {
Thread.sleep(1000L);
return "encoded";
}
}
根据场景选择何时的异常处理逻辑可以使用@SneakyThrows 或者try/catch 在方法底层抛出异常,或者向上抛出,用exceptionhandle处理。
public class DemoThrowExceptionTest {
public ComplexComputer complexComputer = new ComplexComputer();
@Test
public void forLoopTest() throws GeneralSecurityException, InterruptedException {
List<String> items = Lists.newArrayList("foo", "bar", "baz");
for (String item : items) {
complexComputer.encrypt(item);
}
}
@Test
public void throwIOExceptionTest() {
List<String> items = Lists.newArrayList("foo", "bar", "baz");
items.forEach(new Consumer<String>() {
@SneakyThrows
@Override
public void accept(String item) {
complexComputer.encrypt(item);
}
}
);
}
}
Currying in java
科利化是函数式编程一个重要的特性,意思是把接受多个参数的函数变为接受单一参数的函数。
科里方程如下
Named after Haskell Curry
function curry(f) {
return function(a) {
return function(b) {
return f(a, b);
};
};
}
f(a,b) -> f(a)(b)
科利化的使用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jJmav0UZ-1652065085745)(WEBRESOURCE82fb0170acaa43d3d7395e03ec21f05f)]
函数式改造之后
@Test
public void testSimpleStockModelCreation() {
BiFunction<String, String, StockModelA> StockModelCreator = (symbol, name) -> new StockModelA(symbol, name);
Function<String, Function<String, StockModelA>> CurryingStockModelInnerClassCreator =
symbol -> name -> new StockModelA(symbol, name);
}
使用科利化构造器引用之后,传入对应的值
@Test
public void testMoreAttrStockModelCreation() {
Function<String, Function<String, Function<String, Function<BigDecimal, Function<BigDecimal, StockModelB>>>>>
curryingStockModelCreator =
symbol -> name -> desc -> currPrice -> prevPrice -> new StockModelB(symbol, name, desc, currPrice, prevPrice);
StockModelB demoStockModel = curryingStockModelCreator
.apply("SYMBOL")
.apply("NAME")
.apply("DESCRIPTION")
.apply(new BigDecimal("1.00"))
.apply(new BigDecimal("0.99"));
}
部分属性不变时
@Test
public void testPartialModelCreation() {
Function<String, Function<String, Function<String, Function<BigDecimal, Function<BigDecimal, StockModelB>>>>>
curryingStockModelCreator =
symbol -> name -> desc -> currPrice -> prevPrice -> new StockModelB(symbol, name, desc, currPrice, prevPrice);
Function<BigDecimal, Function<BigDecimal, StockModelB>>
partialStockModelCreator =
currPrice -> prevPrice -> curryingStockModelCreator
.apply("SYMBOL")
.apply("NAME")
.apply("DESCRIPTION")
.apply(currPrice)
.apply(prevPrice);
StockModelB fromPartialCreated = partialStockModelCreator
.apply(new BigDecimal("1.00"))
.apply(new BigDecimal("0.99"));
System.out.println(fromPartialCreated);
}
Java Stream API
stream流是可以更方便的使用lambda表达式操作集合的数据结构,并且实现了并行计算等功能。只有在真正需要计算的时候才会执行,“延迟计算”。
stream-category
Stream
- intermiate 中间操作,返回流但是不会计算
- stateless 无状态操作,元素的处理不会受之前或之后元素的影响
- stateful 有状态操作,只有在拿到了所有元素之后才能继续
- terminal 终结操作,会进行计算
- short-circuiting 短路操作,遇到符合的元素就可以得到结果
- un-short-circuiting 非短路操作,只有所有元素都拿到才能计算结果
使用stream流操作Collections
创建stream流
方法一:Stream.generate
使用limit限制无限流生成的数据
@Test
public void streamGenerate() {
Stream<Double> stream = Stream.generate(Math::random);
stream.limit(5).forEach(System.out::println);
}
方法二:Stream.of
@Test
public void streamOf() {
Stream<String> stream1 = Stream.of("foo");
Stream<String> stream2 = Stream.of("foo", "bar", "baz");
Stream<String> stream3 = Arrays.stream(new String[]{"foo", "bar", "baz"});
}
方法三:streamFromCollections
因为map不是继承collection接口,所以要使用stream流遍历时需要entrySet()方法。
@Test
public void streamFromCollections() {
List<Stock> stocks = Lists.newArrayList(
Stock.builder().name("APPLE").symbol("APPL").build(),
Stock.builder().name("Amazon").symbol("AMZN").build(),
Stock.builder().name("Starbucks").symbol("SBUX").build()
);
Stream<Stock> stream1 = stocks.stream();
Map map = new HashMap();
map.entrySet().stream();
}
方法四:Stream.builder
@Test
public void streamBuilder() {
Stream.Builder<Stock> builder = Stream.builder();
builder.accept(Stock.builder().name("APPLE").symbol("APPL").build());
builder.accept(Stock.builder().name("Amazon").symbol("AMZN").build());
builder.accept(Stock.builder().name("Starbucks").symbol("SBUX").build());
Stream<Stock> stockStream = builder.build();
}
操作stream
foreach
@Test
public void streamForEach() {
List<Stock> stocks = Lists.newArrayList(
Stock.builder().name("APPLE").symbol("APPL").build(),
Stock.builder().name("Amazon").symbol("AMZN").build(),
Stock.builder().name("Starbucks").symbol("SBUX").build()
);
stocks.stream().forEach(stock -> stock.setSymbol(stock.getSymbol().toLowerCase()));
System.out.println(stocks);
stocks.forEach(stock -> stock.setSymbol(stock.getSymbol() + "Iterable"));
System.out.println(stocks);
Map<String, Object> map = new HashMap();
map.forEach((k, v)->{});
}
map
map是stream流的中间操作,会返回一个流供下一步操作。
@Test
public void streamMap() {
Stream<String> stream = Stream.of("foo", "bar", "baz");
Stream<Stock> stockStream = stream.map(str -> Stock.builder().name(str).build());
stockStream.forEach(System.out::println);
}
输出结果
Stock(symbol=null, name=foo)
Stock(symbol=null, name=bar)
Stock(symbol=null, name=baz)
collect
把处理好的stream流的元素收集起来,返回一个数据集合。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TUH6C4so-1652065085747)(WEBRESOURCEa3fe625e961cf016ea5a98ab1e5ddab3)]
Collector 中定义了三个泛型,T是规约操作的输入类型,A是中间暂存所用的类型,R是规约操作输出的类型。
public interface Collector<T, A, R> {
Supplier<A> supplier();
BiConsumer<A, T> accumulator();
BinaryOperator<A> combiner();
Function<A, R> finisher();
Set<Characteristics> characteristics();
public static<T, R> Collector<T, R, R> of(Supplier<R> supplier,
BiConsumer<R, T> accumulator,
BinaryOperator<R> combiner,
Characteristics... characteristics) {
Objects.requireNonNull(supplier);
Objects.requireNonNull(accumulator);
Objects.requireNonNull(combiner);
Objects.requireNonNull(characteristics);
Set<Characteristics> cs = (characteristics.length == 0)
? Collectors.CH_ID
: Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH,
characteristics));
return new Collectors.CollectorImpl<>(supplier, accumulator, combiner, cs);
}
public static<T, A, R> Collector<T, A, R> of(Supplier<A> supplier,
BiConsumer<A, T> accumulator,
BinaryOperator<A> combiner,
Function<A, R> finisher,
Characteristics... characteristics) {
Objects.requireNonNull(supplier);
Objects.requireNonNull(accumulator);
Objects.requireNonNull(combiner);
Objects.requireNonNull(finisher);
Objects.requireNonNull(characteristics);
Set<Characteristics> cs = Collectors.CH_NOID;
if (characteristics.length > 0) {
cs = EnumSet.noneOf(Characteristics.class);
Collections.addAll(cs, characteristics);
cs = Collections.unmodifiableSet(cs);
}
return new Collectors.CollectorImpl<>(supplier, accumulator, combiner, finisher, cs);
}
enum Characteristics {
CONCURRENT,
UNORDERED,
IDENTITY_FINISH
}
}
使用
@Test
public void streamCollectToList() {
Stream<String> stream = Stream.of("foo", "bar", "baz");
Stream<Stock> stockStream = stream.map(str -> Stock.builder().name(str).build());
List<Stock> stocks = stockStream.collect(Collectors.toList());
}
@Test
public void streamCollectJoining() {
Stream<String> stream = Stream.of("foo", "bar", "baz");
String result = stream.collect(Collectors.joining("|"));
System.out.println(result);
}
@Test
public void streamCollectGroupBy() {
List<Student> students = getStudentsData();
Map<String, List<Student>> byClass =
students.stream().collect(Collectors.groupingBy(Student::getClassName));
System.out.println(byClass);
}
@Test
public void streamCollectGroupByWithCustomizedDownstream() {
List<Student> students = getStudentsData();
Map<String, Integer> sumScoreByClass =
students.stream().collect(
Collectors.groupingBy(
Student::getClassName,
Collectors.summingInt(Student::getScore)
)
);
System.out.println(sumScoreByClass);
Map<String, Optional<Student>> highestScoreByClass = students.stream().collect(
Collectors.groupingBy(
Student::getClassName,
Collectors.maxBy(Comparator.comparing(Student::getScore))
)
);
System.out.println(highestScoreByClass);
}
filter
stream流的中间操作,filter中实现了一个Predicate 接口。对传入的元素进行判断,断言成功则返回,不成功便丢弃。
@Test
public void streamFilter() {
List<Student> students = getStudentsData();
Stream<Student> scoreOver80Stream = students.stream().filter(student -> student.getScore() > 80);
System.out.println(scoreOver80Stream.collect(Collectors.toList()));
}
findFirst
短路操作。
@Test
public void streamFindFirst() {
List<Student> students = getStudentsData();
Optional<Student> firstGuy = students
.stream()
.filter(student -> student.getScore() > 80)
.findFirst();
System.out.println(firstGuy);
}
@Test
public void streamFindFirst() {
List<Student> students = getStudentsData();
Student firstGuy = students
.stream()
.filter(student -> student.getScore() > 80)
.findFirst()
.orElse(new Student());
System.out.println(firstGuy);
}
peek
与foreach类似,都实现了consumer接口,但是属于中间操作,操作结束之后会把元素重新返回流中。
@Test
public void streamPeek() {
List<Student> students = getStudentsData();
students
.stream()
.peek(student -> student.setScore(student.getScore() + 10))
.peek(System.out::println);
}
flatmap
Function.identity() 是返回元素本身
@Test
public void streamFlatMap() {
String paragraph = "this is a demo paragraph to calculate char occurrences";
String[] words = paragraph.split(" ");
Map<String, Long> occurrence = Arrays.stream(words)
.map(str -> str.split(""))
.flatMap(strAarray -> Arrays.stream(strAarray))
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
System.out.println(occurrence);
}
stream流操作nio
@Test
public void FilesLinesTest() {
Path path = new File(getClass().getResource("/stream/nio_datasource_1.txt").getFile()).toPath();
try (Stream<String> content = Files.lines(path, Charset.defaultCharset())) {
List<String> orderedList = content.map(String::toUpperCase).sorted().collect(Collectors.toList());
System.out.println(orderedList);
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void FilesWalkTest() {
Path rootPath = new File(getClass().getResource("/stream/a").getFile()).toPath();
try (Stream<Path> matched = Files.walk(rootPath, 3)) {
List<Path> matchedPathList = matched.collect(Collectors.toList());
System.out.println(matchedPathList);
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void FilesFindTest() throws IOException {
Path rootPath = new File(getClass().getResource("/stream/a").getFile()).toPath();
try (Stream<Path> matched = Files.find(rootPath, 3, (path, attr) -> path.endsWith("bar.txt"))) {
List<Path> matchedPathList = matched.collect(Collectors.toList());
System.out.println(matchedPathList);
} catch (IOException e) {
e.printStackTrace();
}
}
ParallelStream
public static AtomicInteger counter = new AtomicInteger();
@Test
public void simpleParallel() {
List<Student> students = getLotsOfStudents();
List<Student> studentList = students.parallelStream().peek(student -> counter.getAndIncrement()).collect(Collectors.toList());
System.out.println(students.size());
System.out.println(counter);
}
|