目录
2、内置函数式接口(Built-in Functional Interfaces)
(2)Collectors.groupingBy()和Collectors.partitioningBy()
一、Java8中Interface接口
interface接口 的设计目的是面向接口编程,提高扩展性。
Java8中,接口中除了抽象方法外,还可以定义default默认方法和static静态方法。
default修饰的默认方法,属于实例方法,可以被实现类调用或重写。
(1)调用:实现类必须implements接口,才能调用该接口的default默认方法。
(2)重写:实现类implements不同接口时,接口中存在相同签名的方法(名称、参数、类型完全一致),则实现类必须重写该方法,明确方法定义;
static修饰的静态方法,属于类的静态方法。但它不能被子类继承,只能用interface接口名称调用。
二、Lambda 表达式
Lambda 表达式本质是一个匿名函数,用于把函数作为参数,传入方法中,实现函数式编程风格。
使用Lambda 表达式可以使代码变的更加简洁紧凑。
语法格式:(parameters)-> expression 或 (parameters)->{ statements;}
案例:对集合里面的内容排序
public class Demo01 {
public static void main(String[] args) {
List<String> list = Arrays.asList("as", "ab", "asc", "adcde");
list.sort((o1, o2) -> {
if (o1.length() == o2.length()) {
return o1.compareTo(o2);
}
return o1.length() - o2.length();
});
System.out.println(list);
}
}
输出结果:
三、函数式接口 Functional Interface
1、函数式接口
只有一个抽象方法的接口(可以定义多个非抽象方法)。可以使用@FunctionalInterface接口定义,强化语义规范。
函数式接口,也被称为SAM 接口(Single Abstract Method Interfaces)。
作用:基于函数式接口,可以使用Lambda表达式进行实现,实现函数式编程。
函数式接口Comparator:
示例1:排序(使用Lambda实现函数式接口)
// lambda表达式实现字符串数组排序
Arrays.sort(array, (x1,x2) -> {
if (x1.length() != x2.length()) {
return x1.length() - x2.length();
} else {
return x1.compareTo(x2);
}
} );
示例2:迭代(使用Lambda迭代遍历集合)
List<String> langList = Arrays.asList("Basic","QBasic","c","c++","PowerBuilder","Visual Basic");
langList.forEach((name)->{
System.out.println(name);
});
2、内置函数式接口(Built-in Functional Interfaces)
在 Java 8 中专门有一个包放函数式接口java.util.function,该包下的所有接口都有 @FunctionalInterface 注解,提供函数式编程方式。
(1)Predicate接口
Predicate接口是只有一个参数的返回布尔类型值的断言型接口。该接口包含多种默认方法来将 Predicate 组合成其他复杂的逻辑(比如:与and,或or,非negate)
public class Demo04 {
public static void main(String[] args) {
List<String> langList = Arrays.asList("Basic", "QBasic",
"C", "C++", "PowerBuilder", "C#", "gogo@");
// 判断长度是否等于5
Predicate<String> predicate1 = (lang) -> {
return lang.length() != 5;
};
// 判断是否含有处字母以外的其它字符
Predicate<String> predicate2 = (lang) -> {
for (int i = 0; i < lang.length(); i++) {
if (!Character.isLetter(lang.charAt(i))) {
return true;
}
}
return false;
};
Predicate<String> predicate3 = predicate1.and(predicate2);// 逻辑与
Predicate<String> predicate4 = predicate1.or(predicate2);// 逻辑或
Predicate<String> predicate5 = predicate1.negate();// 逻辑非
// 遍历判断
langList.forEach((lang) -> {
// 根据不同的需求选用不同的实例对象
if (predicate5.test(lang)) { // 输出长度等于5的字符串
System.out.println(lang);
}
});
}
}
输出结果:
(2)Function接口
Function 接口接受一个参数并生成结果。默认方法可用于将多个函数链接在一起(compose, andThen)。
public class Demo06 {
public static void main(String[] args) {
List<String> langList = Arrays.asList("Basic", "QBasic", "C", "C++", "PowerBuilder", "C#", "gogo@");
Function<String, String> function1 = (lang)->{
return lang.toLowerCase();// 将集合中的字符转换为小写
};
Function<String, String> function2 = (lang)->{
return String.format("<%s>", lang);// 将集合中的每个字符串两边分别拼接"<"和">"
};
// 合并操作
Function<String, String> function3 = function1.andThen(function2);// 先执行function1,再执行function2
Function<String, String> function4 = function1.compose(function2);// 先执行function2,再执行function1
langList.stream().map(function4).forEach((lang)->{
System.out.println(lang);
});
}
}
输出结果:
(3)Comparator接口
比较器接口,用于比较指定元素值的大小。Java8版本中,添加了多个新的default方法,用于比较器合并、反转等操作。
public class Demo07 {
public static void main(String[] args) {
List<String> langList = Arrays.asList("Basic", "QBasic", "c", "C++", "PowerBuilder", "C#", "gogo@");
// 按照内容比较
Comparator<String> comparator1 = (x,y)->{
return x.compareTo(y);
};
// 按照长度比较
Comparator<String> comparator2 = (x,y)->{
return x.length()-y.length();
};
// 先比较内容,再比较长度
Comparator<String> comparator3 = comparator1.thenComparing(comparator2);
// 按照长度比较(逆序)
Comparator<String> comparator4 = comparator2.reversed();
langList.sort(comparator4);
System.out.println(langList);
}
}
输出结果:
四、Stream流
java.util.Stream 表示能应用在一组元素上一次执行的操作序列。
Stream操作分为中间操作或者最终操作两种,最终操作返回一特定类型的计算结果,而中间操作返回Stream本身,可以连续完成多个操作。
1、Filter 过滤
过滤通过一个predicate接口来过滤并只保留符合条件的元素,该操作属于中间操作。所以过滤后的结果,可以继续进行其它Stream操作(例如forEach,forEach需要一个函数来对过滤后的元素依次执行。forEach是一个最终操作)。
public class Demo03 {
public static void main(String[] args) {
List<String> langList = Arrays.asList("Basic", "QBasic", "C", "C++", "PowerBuilder", "C#");
// 过滤出长度等于1或等于2的字符串输出
langList.stream().filter((lang) -> {
return lang.length() == 1 || lang.length() == 2;
}).forEach((lang) -> {
System.out.println(lang);
});
}
}
输出结果:
2、Sorted 排序
排序是一个 中间操作,返回的是排序好后的 Stream。(不影响原数据)
public class Demo18 {
public static void main(String[] args) {
List<String> list = Arrays.asList("abc", "def", "gkh", "abc");
// 排序并遍历
list.stream().sorted().forEach((str) -> {
System.out.println(str);
});
}
}
输出结果:
3、Map 映射
映射是一个中间操作, 会将元素根据指定的 Function 接口来依次将元素转成另外的对象。
public class Demo05 {
public static void main(String[] args) {
List<String> langList = Arrays.asList("Basic", "QBasic", "C", "C++", "PowerBuilder", "C#", "gogo@");
langList.stream().map((lang) -> {
// return lang.toLowerCase();// 将集合中的元素转换为小写
return lang.length();// 集合中元素的长度 => 输出相应的长度
}).forEach((lang) -> {
System.out.println(lang);
});
}
}
输出结果:
4、Match 匹配
Stream提供了多种匹配操作,允许检测指定的Predicate是否匹配整个Stream。所有的匹配操作都是最终操作,并返回一个 boolean 类型的值。
public class Demo08 {
public static void main(String[] args) {
List<String> list = Arrays.asList("七里香", "双截棍", "夜曲", "青花瓷");
// 局部匹配:是否存在1个名称长度为2的歌曲
boolean isContains1 = list.stream().anyMatch((music) -> {
return music.length() == 2;
});
// 全局匹配:所有歌曲名称的长度是否都为2
boolean isContains2 = list.stream().allMatch((music) -> {
return music.length() == 3;
});
// 全局匹配:所有歌曲名称的长度是否都不为2
boolean isContains3 = list.stream().noneMatch((music) -> {
return music.length() == 3;
});
System.out.println(isContains1);
System.out.println(isContains2);
System.out.println(isContains3);
}
输出结果:
5、Count 计数
计数是一个 最终操作,返回Stream中元素的个数,返回值类型是 long。
public class Demo19 {
public static void main(String[] args) {
String[] array = { "七里香", "双截棍", "夜曲", "青花瓷" };
// 将数组转换为流
long count = Arrays.stream(array).filter((music) -> {// 过滤出名称长度为3的歌曲
return music.length() == 3;
}).count();// 统计过滤后的结果数量
System.out.println(count);
}
}
输出结果:
6、Distinct 去重
public class Demo09 {
public static void main(String[] args) {
String[] array = { "七里香", "双截棍", "夜曲", "青花瓷", "听妈妈的话", "七里香", "夜曲" };
System.out.println("名字最长的歌曲是:");
// distinct():去重
// findFirst().get():得到第一个元素的值
// max():获取最大值
String musicName1 = Arrays.stream(array).distinct().sorted((x, y) -> {
if (x.length() == y.length()) {
return x.compareTo(y);
}
return y.length() - x.length();
}).findFirst().get();
System.out.println(musicName1);
String musicName2 = Arrays.stream(array).distinct().max((x, y) -> {
return x.length() - y.length();
}).get();
System.out.println(musicName2);
System.out.println("名字最短的歌曲是:");
String musicName3 = Arrays.stream(array).distinct().min((x, y) -> {
return x.length() - y.length();
}).get();
System.out.println(musicName3);
}
}
输出结果:
7、Collect 收集
收集是一个 最终操作,返回Stream中元素集合,返回值类型是集合(List、Set、Map)或字符串。
将Stream中的元素,收集至新集合:
Collectors.toList()
Collectors.toSet()
Collectors.toMap()
public class Demo10 {
public static void main(String[] args) {
String[] array = { "七里香", "双截棍", "夜曲", "青花瓷", "听妈妈的话", "七里香", "夜曲" };
// 去重后将元素收集成一个List集合
List<String> list = Arrays.stream(array).distinct().collect(Collectors.toList());
System.out.println(list);
// 将元素收集成一个Set集合(自动去重)
Set<String> set = Arrays.stream(array).collect(Collectors.toSet());
System.out.println(set);
// 去重后将元素收集成一个LinkedList集合
LinkedList<String> linkedList = Arrays.stream(array).distinct()
.collect(Collectors.toCollection(LinkedList::new));
System.out.println(linkedList);
// 将元素收集成一个LinkedHashSet集合(自动去重,有序)
LinkedHashSet<String> linkedHashSet = Arrays.stream(array).collect(Collectors.toCollection(LinkedHashSet::new));
System.out.println(linkedHashSet);
// 去重后将元素收集成一个Map集合
// 方式一:
Map<String, Integer> map1 = Arrays.stream(array).distinct().collect(Collectors.toMap((music) -> {
return music;
}, (music) -> {
return music.length();
}));
System.out.println(map1);
// 方式二:
Map<String, Integer> map2 = Arrays.stream(array).distinct()
.collect(Collectors.toMap(music -> music, music -> music.length()));
System.out.println(map2);
}
}
输出结果:
(1)Collectors.mapping()
将Stream中的元素,映射后,收集至新集合。
public class Demo11 {
public static void main(String[] args) {
List<String> strList = Arrays.asList("32", "54", "13", "53");
List<Integer> numList = strList.stream()
.collect(Collectors.mapping(s -> Integer.parseInt(s) * 10, Collectors.toList()));
System.out.println(numList);
}
}
输出结果:
(2)Collectors.groupingBy()和Collectors.partitioningBy()
Collectors.groupingBy():将Stream中的元素,分组后,收集至Map集合;
Collectors.partitioningBy():将Stream中的元素,按照判断规则,统计分区后,收集至Map集合。
public class Demo12 {
public static void main(String[] args) {
List<String> list = Arrays.asList("曹操", "张飞", "刘备", "曹植", "刘禅", "曹丕", "张辽", "孙权", "孙尚香");
// 按照姓氏分组
Map<Character, List<String>> map1 = list.stream().collect(Collectors.groupingBy(name -> name.charAt(0)));
System.out.println(map1);
// 按照名字长度是否为2分区
Map<Boolean, List<String>> map2 = list.stream().collect(Collectors.partitioningBy(name -> name.length() == 2));
System.out.println(map2);
}
}
输出结果:
8、Statistics 统计
统计是一个最终操作,返回Stream中元素的各类统计信息,返回值类型是 XXXConsumer。
public class Demo13 {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1,2,3,4);
Stream<Integer> stream = list.stream();
IntStream intStream = stream.mapToInt(x->x);
IntSummaryStatistics statistics = intStream.summaryStatistics();
System.out.println("最大值:"+statistics.getMax());
System.out.println("最小值:"+statistics.getMin());
System.out.println("平均值:"+statistics.getAverage());
System.out.println("累加和:"+statistics.getSum());
}
}
输出结果:
9、Parallel Streams 并行流
Stream有串行和并行两种,串行Stream上的操作是在一个线程中依次完成,而并行Stream则是在多个线程上同时执行。
public class Demo14 {
public static void main(String[] args) {
int max = 1000000;
List<String> list = new ArrayList<String>(max);
for (int i = 0; i < max; i++) {
list.add(UUID.randomUUID().toString());
}
long start = System.currentTimeMillis();
long count = list.parallelStream().sorted().count();
long end = System.currentTimeMillis();
System.out.println(end - start);
System.out.println(count);
}
}
输出结果:
五、日期时间
Java 8在java.time 包下包含一个全新的日期和时间API。
(1)LocalDateTime //日期+时间 format: yyyy-MM-ddTHH:mm:ss.SSS
(2)LocalDate //日期 format: yyyy-MM-dd
(3)LocalTime //时间 format: HH:mm:ss
1、格式化
public class Demo15 {
public static void main(String[] args) {
LocalDate localDate = LocalDate.now();
LocalTime localTime = LocalTime.now().withNano(0);
LocalDateTime localDateTime = LocalDateTime.now();
System.out.println(String.format("当前日期:%s", localDate));
System.out.println(String.format("当前时间:%s", localTime));
System.out.println(String.format("当前时间:%s", localDateTime));
}
输出结果:
2、字符串转日期格式
public class Demo20 {
public static void main(String[] args) {
LocalDate date1 = LocalDate.of(2023, 8, 20);
LocalDate date2 = LocalDate.parse("2023-08-20");
System.out.println(date1);
System.out.println(date2);
LocalDateTime dateTime1 = LocalDateTime.of(2023, 8, 20, 12, 12, 22);
LocalDateTime dateTime2 = LocalDateTime.parse("2023-08-20T12:12:22");
System.out.println(dateTime1);
System.out.println(dateTime2);
LocalTime time1 = LocalTime.of(15, 22, 14);
LocalTime time2 = LocalTime.parse("15:22:14");
System.out.println(time1);
System.out.println(time2);
}
}
输出结果:
3、日期计算
public class Demo16 {
public static void main(String[] args) {
// 计算一周后的日期
LocalDate localDate = LocalDate.now();
// 方法1
LocalDate after1 = localDate.plus(1, ChronoUnit.WEEKS);
System.out.println("一周后日期:" + after1);
// 方法2
LocalDate after2 = localDate.plusWeeks(1);
System.out.println("一周后日期:" + after2);
// 计算两个日期间隔多少天,计算间隔多少年,多少月
LocalDate date1 = LocalDate.parse("2023-02-26");
LocalDate date2 = LocalDate.parse("2023-12-23");
Period period = Period.between(date1, date2);
System.out.println("date1 到 date2 相隔:" + period.getYears() + "年" + period.getMonths() + "月" + period.getDays() + "天");
// 计算两个日期间隔多少天
long day = date2.toEpochDay() - date1.toEpochDay();
System.out.println(date2 + "和" + date2 + "相差" + day + "天");
}
}
输出结果:
4、获取指定日期
public class Demo17 {
public static void main(String[] args) {
LocalDate today = LocalDate.now();
// 获取当前月第一天:
LocalDate firstDayOfThisMonth = today.with(TemporalAdjusters.firstDayOfMonth());
System.out.println("当前月第一天:" + firstDayOfThisMonth);
// 获取本月最后一天
LocalDate lastDayOfThisMonth = today.with(TemporalAdjusters.lastDayOfMonth());
System.out.println("本月最后一天:" + lastDayOfThisMonth);
// 获取下一天:
LocalDate nextDay = lastDayOfThisMonth.plusDays(1);
System.out.println("下一天(次月第一天):" + nextDay);
// 获取当年最后一天
LocalDate lastday = today.with(TemporalAdjusters.lastDayOfYear());
System.out.println("当前月第一天:" + lastday);
// 获取当年最后一个周日
LocalDate lastMondayOfThisYear = lastday.with(TemporalAdjusters.lastInMonth(DayOfWeek.SUNDAY));
System.out.println("当年最后一个周日:" + lastMondayOfThisYear);
}
}
输出结果:
总结
1、Java 8中,接口中除了抽象方法外,还可以定义default默认方法和static静态方法。
2、Java 8支持使用Lambda表达式,实现函数式编程风格。
3、函数式接口
(1)Predicate、Function、Comparator;
(2)通过链式编程,使得它可以方便地对数据进行链式处理;
(3)方法参数都是函数式接口类型;
(4)一个 Stream 只能操作一次,操作完就关闭了,继续使用这个 Stream 会报错;
(5)Stream 不保存数据,不改变数据源。
4、Java 8在java.time 包下包含一个全新的日期和时间API。