-
List类型抽象化
public static <T> List<T> filter(List<T> list, Predicate<T> p){
return list.stream().filter(p::test).collect(Collectors.toList());
}
public static <T> List<T> filter(List<T> list, Predicate<T> p){
List<T> result = new ArrayList<>();
for(T e: list){
if(p.test(e)){
result.add(e);
}
}
return result;
}
关键字: 谓词 行为参数化 匿名类 Lambda 策略模式
-
Lambda表达式
函数式接口就是只定义一个抽象方法的接口,如:Runnable,Callable,Comparator,ActionListener
@FunctionalInterface
-
Predicate,Consumer,Function
Predicate :boolean test(T t)
Consumer :void accept(T t)
Function : R apply(T t)
环绕执行模式-
流的创建
Stream<String> echos = Stream.generate(() -> "echos");
//不停的打印echos
echos.forEach(System.out::println);
Stream<Double> echos = Stream.generate(() -> Math.random());
//不停的打印0到1的随机数
echos.forEach(System.out::println);
Stream<BigInteger> echos = Stream.iterate(BigInteger.ZERO,n -> n.add(BigInteger.ONE));
//不停的打印0,1,2,3,4序列值
echos.forEach(System.out::println);
-
自动关闭流 try-with-resources写法
@Test
public void testTryWithResource() {
Path path = Paths.get("E:\\apache-maven-3.5.3\\conf\\settings.xml");
try (Stream<String> lines = Files.lines(path)) {
lines.forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
}
流只能遍历一次,集合可以循环遍历
-
Stream API
-
筛选
filter
- 查找文件中单词比较长的个数
@Test
public void testFindLongWords() throws IOException {
String fileName = "D:\\1.txt";
String content = new String(Files.readAllBytes(Paths.get(fileName)), StandardCharsets.UTF_8);
List<String> contents = Arrays.asList(content.split("[\\P{L}]+"));
for (int j = 0; j < 5; j++) {
Instant startTime = Instant.now();
int count = 0;
for (String word : contents) {
if (word.length() >= 12) count++;
}
System.out.println("count:" + count + " for:" + Duration.between(startTime, Instant.now()).toMillis());
startTime = Instant.now();
long c = contents.stream().filter(w -> w.length() >= 12).count();
System.out.println("count:" + c + " stream:" + Duration.between(startTime, Instant.now()).toMillis());
startTime = Instant.now();
c = contents.parallelStream().filter(w -> w.length() >= 12).count();
System.out.println("count:" + c + " parallelStream:" + Duration.between(startTime, Instant.now()).toMillis());
System.out.println("============================");
}
}
distinct
count
max
min
-
截断流
limit
-
跳过
skip
-
映射
flatmap(Arrays::stream) 将各个生成流扁平化为单个流
map(Arrays::stream) 每个数组生成一个单独的流
创建一个新版本而不是修改
-
匹配 查找
allMatch anyMatch noneMatch findFirst findAny
List<String> words = Arrays.asList("aaa", "bbb", "ccc","bcasd","ad","bc","bsd","cs");
Optional<String> maxWords = words.stream().max(String::compareToIgnoreCase);
maxWords.ifPresent(System.out::println);
//找到第一个满足 条件的
Optional<String> b = words.stream().filter(a -> a.startsWith("b")).findFirst();
b.ifPresent(System.out::println);
//findAny 在并行的时候 可以获得更好的性能
Optional<String> b1 = words.stream().parallel().filter(a -> a.startsWith("b")).findAny();
b1.ifPresent(System.out::println);
boolean b2 = words.stream().anyMatch(a -> a.startsWith("b"));
System.out.println(b2);
List<String> newWords = new ArrayList<>();
//不为空时干什么,用ifPresent代替if,map可以获取操作的返回值,但是也要注意为空的情况
b1.ifPresent(bb -> Collections.addAll(newWords,bb));
Optional<Boolean> aBoolean = b1.map(bb -> Collections.addAll(newWords, bb));
aBoolean.ifPresent(System.out::println);
System.out.println(newWords);
//提供一个为空时的值
String s = b1.orElse("");
System.out.println(s);
//提供一个计算为空的方法
System.out.println(b1.orElseGet(() -> System.getProperty("user.dir")));
短路
-
切片
-
归约
reduce
@Test
public void testReduce() {
Stream<Integer> values = Stream.of(1, 3, 4, 6, 234, 434, 5);
values.reduce(Integer::sum).ifPresent(System.out::println);
//java.lang.IllegalStateException: stream has already been operated upon or closed
//values.reduce((x, y) -> x + y).ifPresent(System.out::println);
//reduce(0,xxx),返回的时一个值,不是Optional
Integer sum = Stream.of(1, 3, 4, 6, 234, 434, 5).reduce(0, Integer::sum);
System.out.println(sum);
}
//计算所有 单词的长度之和
System.out.println(words.stream().mapToInt(String::length).sum());
等同于下面的表达式
Integer reduce = words.stream().reduce(0, (total, word) -> total + word.length(), (total1, total2) -> total1 + total2);
System.out.println(reduce);
reduce 操作应该是联合的,即 (x op y) op z = x op (y op z),比如减法就不是联合的,它和先后顺序是有关系的.比如 (6 -3) -2 ≠ 6 - (3 -2)
- 转数组
Long[] longs = Stream.of(1L, 3L, 4L, 6L).toArray(Long[]::new);
for(int i = 0 ;i <longs.length ;i++) {
System.out.println(longs[i]);
}
- 聚合操作
@Test
public void testReduceHashSet() {
Stream<String> values = Stream.of("bb", "sss","aa", "cksdl","aa");
// HashSet<String> sets = values.collect(HashSet::new, HashSet::add, HashSet::addAll);
// List<String> list = values.collect(Collectors.toList());
// list.forEach(System.out::println);
// Set<String> set = values.collect(Collectors.toSet());
// set.forEach(System.out::println);
// System.out.println(values.collect(Collectors.joining(",")));
// TreeSet<String> treeSet = values.collect(Collectors.toCollection(TreeSet::new));
// treeSet.forEach(System.out::println);
//统计信息 LongSummaryStatistics{count=5, sum=14, min=2, average=2.800000, max=5}
// LongSummaryStatistics summaryStatistics = values.collect(Collectors.summarizingLong(String::length));
// System.out.println(summaryStatistics.toString());
// values.forEachOrdered(System.out::println);
//peek能继续用这个流,不是终止操作
long count = values.peek(System.out::println).count();
System.out.println(count);
}
- toMap
第1个参数,得出Key值的计算方法
第2个参数,得出Value 值的计算方法
第3个参数,如果合并也有的新值
@Test
public void testCollectMap() {
Stream<Locale> locales = Stream.of(Locale.getAvailableLocales());
Map<String, String> languageNames = locales.collect(Collectors.toMap(Locale::getDisplayLanguage, l -> l.getDisplayLanguage(l), (existingValue, newValue) -> existingValue));
languageNames.forEach((s, s2) -> {
System.out.println(s + "-->" + s2);
});
}
- toMap<String,HashSet>
@Test
public void testCollectMap1() {
Stream<Locale> locales = Stream.of(Locale.getAvailableLocales());
Map<String, Set<String>> languageCountrys = locales.collect(Collectors.toMap(Locale::getDisplayCountry, l -> Collections.singleton(l.getDisplayLanguage()), (a, b) -> {
Set<String> r = new HashSet<>(a);
r.addAll(b);
return r;
}));
languageCountrys.forEach((k, v) -> {
System.out.println(k + "->" + v);
});
}
-
分组和分片
@Test
public void testGroupingBy1() {
Map<String, List<Locale>> localeCountries = Stream.of(Locale.getAvailableLocales()).collect(Collectors.groupingBy(Locale::getCountry));
localeCountries.forEach((k, v) -> {
System.out.println(k + "->" + v);
});
Map<Boolean, List<Locale>> cnCountries = Stream.of(Locale.getAvailableLocales()).collect(Collectors.partitioningBy(l -> l.getLanguage().equals("en")));
cnCountries.forEach((k, v) -> {
System.out.println(k + "->" + v);
});
}
Map<String, Set<Locale>> localeCountriesSet = Stream.of(Locale.getAvailableLocales()).collect(Collectors.groupingBy(Locale::getCountry,Collectors.toSet()));
localeCountries.forEach((k, v) -> {
System.out.println(k + "->" + v);
});
-
数值流
-
无限流
-
什么时候用Lambda?Lambda的好处?
- 延迟调用,比如记录日志的logger.info ,参数传入一个Supplier<String>, 就会延迟调用.
- Lambda表达式的参数?
- 函数式接口
函数式接口 | 参数类型 | 返回类型 | 抽象方法名 | 描述 | 其他方法 |
---|---|---|---|---|---|
Runnable | run | 执行一个没有参数 ,没有返回值的操作 | |||
Supplier<T> | T | get | 提供一个T类型的值 | ||
Consumer<T> | T | 空 | accept | 消费一个T类型的值 | andThen |
BiConsumer<T, U> | T t, U u | 空 | accept | 消费两个参数的值 | andThen |
Function<T, R> | T | R | apply | 输入T转换成R | identity,andThen,compose |
BiFunction<T, U, R> | T, U | R | apply | 输入两个参数,返回一个值R | andThen |
UnaryOperator<T> | T | T | apply | 继承Function<T,T>,对类型T进行的一元操作 | compose,andThen,identity |
BinaryOperator<T> | T,T | T | apply | 继承BiFunction<T,T,T>,对类型T进行二元操作 | maxBy,minBy,andThen |
Predicate<T> | T | boolean | test | 一个计算Boolean值的函数 | and,negate,or,isEqual |
BiPredicate<T, U> | T t, U u | boolean | test | 一个计算Boolean值的函数,2个参数 | and,negate,or |
- 返回函数 UnaryOperator
-
组合 compose
- 并行操作
-
处理异常
-
Optional
lambda表达式内的闭包是不能被修改的.
public static Optional<Double> inverse(Double x) {
return x == 0 ? Optional.empty() :Optional.of(1/x) ;
// return Optional.ofNullable(x);
}
public static Optional<Double> squareRoot(Double x) {
return x < 0 ? Optional.empty() : Optional.of(Math.sqrt(x));
}
@Test
public void testFlatMap() {
Optional<Double> aDouble = inverse(4.0).flatMap(StringTest::squareRoot);
aDouble.ifPresent(System.out::println);
Optional<Double> aDouble1 = Optional.of(0.0).flatMap(StringTest::inverse).flatMap(StringTest::squareRoot);
aDouble1.ifPresent(System.out::println);
}
flatmap 将两个流展开.使得可以在Optional连着Optional运算下去
安全的Null值相等比较
CompareDemo demo = null;
CompareDemo demo1 = new CompareDemo("","");
System.out.println(Objects.equals(demo,demo1));
Objects.toString() 会处理好Null值的情况,尽量用之,或者用String.valueOf()
-
文件的操作
Path类
强大的Files工具类
Path path = Paths.get("D:\\Reference");
try (Stream<Path> entries = Files.list(path)){
entries.forEach(System.out::println);
}
//walk 递归
try (Stream<Path> entries = Files.walk(path)){
entries.forEach(System.out::println);
}
// Files.find() 查找时用
反射的一堆异常 可以用
ReflectiveOperationException
代替了-
其他的改进
//用分隔符连接字符串
String.join(",", "a", "b", "c")
System.out.println(String.join("_",Arrays.asList("a", "b", "c")));
System.out.println(Math.floorMod(-5,3)); // 1
System.out.println(Math.floorMod(5,3)); //2
System.out.println(-5 % 3); //-2
Math,StrictMath 数学工具类
-
注解
新增了两个特性
- 可以使用重复注解,但是要注意在处理注解时要处理两种注解,包括注解容器
@Repeatable(Authors.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface Author {
String name();
}
@Retention(RetentionPolicy.RUNTIME)
public @interface Authors {
Author[] value();
}
-
并发增强
- LongAccumulator vs AtomicLong
LongAccumulator.accumulate(5) LongAdder.increment, 这两个的条件是:最终的结果必须与中间值的组合顺序 无关,LongAccumulator是在get时汇总,LongAdder方法是在sum时汇总
- merge compute 更新 ConcurrentHashMap,search ,reduce , forEach
public static AtomicLong largest = new AtomicLong();
@Test
public void testAtomicLong() {
largest.set(Math.max(largest.get(),5)); //错误-竞争条件
//如果另一个线程也在更新这个值,很有可能它已经捷足先登了
// ,那么随后的compareAndSet会返回FALSE,这时程序要再次循环
long oldValue;
long newValue;
do {
oldValue = largest.get();
newValue = Math.max(oldValue, 5);
} while (!largest.compareAndSet(oldValue, newValue));
//Java8方法,查看代码,是源码已经封装了上面的循环方法
largest.updateAndGet(x -> Math.max(x, 5));
largest.accumulateAndGet(5, Math::max);
}
-
ConcurrentHashMap
@Test
public void testConcurrentHashMap() {
//线程不安全,操作的顺序不是原子的,所以结果无法预测
Map<String, Long> map = new ConcurrentHashMap<>();
Long oldValue = map.get("word");
Long newValue = oldValue == null ? 1 : oldValue + 1;
map.put("word", newValue);
//补救方法
do {
oldValue = map.get("word");
newValue = oldValue == null ? 1 : oldValue + 1;
} while (!map.replace("word", oldValue, newValue));
//或者用ConcurrentHashMap<String,AtomicLong>
Map<String, AtomicLong> map1 = new ConcurrentHashMap<>();
map1.putIfAbsent("word", new AtomicLong());
map1.get("word").incrementAndGet();
//或者用ConcurrentHashMap<String,LongAdder>
Map<String, LongAdder> map2 = new ConcurrentHashMap<>();
map2.putIfAbsent("word",new LongAdder()).add(1);
}
-
Set
Set<String> words = ConcurrentHashMap.<String>newKeySet();
words.add("Java");
words.add("2");
words.add("3");
words.forEach(System.out::println);
-
Future
CompletableFuture 流水线
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> readPage(url));
future.thenApply()
-
日期的改进
- 所有的java.time的类都是不可变的,线程安全
- 一个瞬间 Instant 是时间线上的一个点
- 持续时间 Duration 是两个瞬间 Instant之间的时间
- Java里,每天都是86400 = 24 * 60 * 60秒,没有闰
- LocalDateTime没有时区信息
- Temporal Adjuster可以处理常用的日历运算
- ZonedDateTime 是指定时区的某个时间点
- 当处理带时区的时间时,请使用 时段 Period ,而不是 Duration,以便考虑夏令时的变化
- 使用 DateTimeFormatter 来格式化和解析时间
- Instant
Instant now = Instant.now();
try {
sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
Instant end = Instant.now();
Duration duration = Duration.between(now, end);
System.out.println(duration.toMillis());//501
//检查一个算法是否比另一个算法快十倍
Duration time1 = Duration.between(Instant.now(), Instant.now().plusSeconds(21));
Duration time2 = Duration.between(Instant.now(), Instant.now().plusSeconds(2));
boolean isFaster = time2.multipliedBy(10).minus(time1).isNegative();
System.out.println(isFaster);
- LocalDate
for(int i = 2000;i<2020;i++) {
System.out.println(i + ": " + LocalDate.of(i,1,1).plusDays(255));
}
LocalDate birthday = LocalDate.of(2012,1,2);
System.out.println(birthday.plusYears(1));
System.out.println(birthday.plus(Period.ofYears(1)));
System.out.println(birthday.plus(Duration.ofDays(365))); //报错 UnsupportedTemporalTypeException
System.out.println(LocalDate.of(2016,1,31).plusMonths(1)); //2016-02-29
System.out.println(LocalDate.of(2016,3,31).minusMonths(1)); //2016-02-29
//本周,从星期日开始算第一天
LocalDate localDate = LocalDate.of(2018, 3, 8).minusDays(LocalDate.of(2018, 3, 8).getDayOfWeek().getValue());
System.out.println(localDate.getDayOfWeek() +" : " +localDate.toString());
System.out.println(localDate.plusDays(6).getDayOfWeek()+" : " + localDate.plusDays(6).toString());
- Date 转 LocalDateTime
Date date = new Date();
Instant instant = date.toInstant();
ZoneId zoneId = ZoneId.systemDefault();
LocalDateTime localDateTime = instant.atZone(zoneId).toLocalDateTime();
System.out.println("Date = " + date);
System.out.println("LocalDateTime = " + localDateTime);
- LocalDateTime 转 Date
ZoneId zoneId = ZoneId.systemDefault();
LocalDateTime localDateTime = LocalDateTime.now();
ZonedDateTime zdt = localDateTime.atZone(zoneId);
Date date = Date.from(zdt.toInstant());
System.out.println("LocalDateTime = " + localDateTime);
System.out.println("Date = " + date);
- 每个月的第二个周二
LocalDate firstTuesday = LocalDate.of(2018, 3, 1).with(TemporalAdjusters.nextOrSame(DayOfWeek.TUESDAY));
- TemporalAdjusters用法
public void testFirstDay() {
LocalDate ld = LocalDate.now();
System.out.println(String.join("firstDayOfMonth",ld.with(TemporalAdjusters.firstDayOfMonth()).toString()));
System.out.println(String.join("firstDayOfMonth",ld.with(TemporalAdjusters.firstDayOfYear()).toString()));
System.out.println(String.join("firstDayOfMonth",ld.with(TemporalAdjusters.firstDayOfNextMonth()).toString()));
System.out.println(String.join("firstDayOfMonth",ld.with(TemporalAdjusters.firstDayOfNextYear()).toString()));
System.out.println();
}
- 计算某一个月的最后一个工作日
@Test
public void testFirstWorkDay() {
int year = 2018;
int month = 3;
LocalDate ld = LocalDate.of(year, month, 1);
int daysOfMonth = ld.with(TemporalAdjusters.lastDayOfMonth()).getDayOfMonth();
LocalDate lastWorkDay = null;
for (int i = daysOfMonth; i > 0; i--) {
DayOfWeek dayOfWeek = LocalDate.of(year, month, i).getDayOfWeek();
if(dayOfWeek.getValue() >= 6){
continue;
}
lastWorkDay = LocalDate.of(year, month, i);
break;
}
System.out.println(lastWorkDay.toString());
}
- 某月的最后一个星期几
@Test
public void testLastInMonth() {
//某月的最后一个星期几
int year = 2018;
int month = 3;
DayOfWeek dayOfWeek = DayOfWeek.MONDAY;
LocalDate ld = LocalDate.of(year, month, 1);
System.out.println(ld.with(TemporalAdjusters.lastInMonth(dayOfWeek)).toString());
}
- 某一个指定日期的上一个(下一个)星期几
@Test
public void testNextOrSame() {
int year = 2018;
int month = 3;
DayOfWeek dayOfWeek = DayOfWeek.SATURDAY;
LocalDate ld = LocalDate.of(year, month, 31);
System.out.println(ld.with(TemporalAdjusters.nextOrSame(dayOfWeek)).toString()); //2018-03-31本身就是星期六,所以就同一天了
System.out.println(ld.with(TemporalAdjusters.next(dayOfWeek)).toString());//2018-04-07 本身是星期六就下个
System.out.println(ld.with(TemporalAdjusters.previousOrSame(dayOfWeek)).toString());//2018-03-31
System.out.println(ld.with(TemporalAdjusters.previous(dayOfWeek)).toString());//2018-03-24
System.out.println(ld.with(TemporalAdjusters.previousOrSame(DayOfWeek.SUNDAY)).toString());
System.out.println(ld.with(TemporalAdjusters.previous(DayOfWeek.SUNDAY)).toString());
}
- 计算下一个工作日
// TemporalAdjusters.ofDateAdjuster 转换类型
@Test
public void testNextWorkDay1() {
TemporalAdjuster NEXT_WORKDAY = TemporalAdjusters.ofDateAdjuster(ld -> {
LocalDate result = ld;
do {
result = result.plusDays(1);
} while (result.getDayOfWeek().getValue() >= 6);
return result;
});
LocalDate ld = LocalDate.now().with(NEXT_WORKDAY);
System.out.println(ld.toString());
}
@Test
public void testNextWorkDay() {
TemporalAdjuster NEXT_WORKDAY = (ld) -> {
LocalDate result = (LocalDate) ld;
do {
result = result.plusDays(1);
} while (result.getDayOfWeek().getValue() >= 6);
return result;
};
LocalDate ld = LocalDate.now().with(NEXT_WORKDAY);
System.out.println(ld.toString());
}
- ZonedDateTime
@Test
public void testAvailableZone() {
//获取所有可用的时区 (IANA)
ZoneId.getAvailableZoneIds().stream().sorted().forEach(System.out::println);
System.out.println(ZoneId.systemDefault()); //Asia/Shanghai
ZonedDateTime zdt = ZonedDateTime.now();
//获取与UTC(格林威治皇家天文台的时间)之间的时差
System.out.println(zdt.getOffset().toString());
//Europe/Berlin
ZonedDateTime meeting = ZonedDateTime.of(LocalDate.of(2013, 10, 25), LocalTime.of(2, 30), ZoneId.of("Europe/Berlin"));
ZonedDateTime nextMeeting = meeting.plusDays(7);
System.out.println(nextMeeting); //2013-11-01T02:30+01:00[Europe/Berlin]
nextMeeting = meeting.plus(Duration.ofDays(7));
System.out.println(nextMeeting); //2013-11-01T01:30+01:00[Europe/Berlin] 不会处理夏令时
nextMeeting = meeting.plus(Period.ofDays(7));
System.out.println(nextMeeting); //2013-11-01T02:30+01:00[Europe/Berlin]
}
-
格式化
字符串转日期
@Test
public void testParse() {
LocalDate ld = LocalDate.parse("2018-03-16");
System.out.println(ld.toString());
ZonedDateTime zdt = ZonedDateTime.parse("2018-03-16 03:31:00+0800",DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ssxx"));
System.out.println(zdt);
zdt = ZonedDateTime.parse("2018-03-16 03:31:00+08:00",DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ssxxx"));
System.out.println(zdt);
}
-
与遗留的日期的转换