Java基础 — 正则表达式+函数式编程
一、正则表达式
正则表达式
正则表达式是用来匹配字符串的,Java内置了正则表达式的支持。
正则表达式可以用字符串来描述规则,用来匹配字符串。一个正则表达式就是一个描述规则的字符串。
使用正则表达式可以快速判断给定的字符串是否符合匹配规则。
正则表达式是一套标准,可以用于任何语言,Java标准库内置了正则表达式引擎。
String regex="20\\d\\d";System.out.println("2020".matches(regex));
匹配规则
如果正则表达式中有特殊字符,需要用"/"转义。
想匹配非ASCII字符,如中文,用\u####的十六机制表示。
"."可以匹配任意字符,仅限一个字符。
"\d"可以匹配0~9数字,仅限一个字符。
"\w"可以匹配一个字母,数字或者下划线。
"\s"可以匹配一个空格字符、Tab键。
"\D"可以匹配一个非数字。
修饰符(重复匹配)
"A*":可以匹配任意个字符,如"A\d*",可以匹配为A,A0,A380;
"A+":可以匹配至少1个字符;
"A?":可以匹配0个或1个字符;
"A{n}":可以精确指定n个字符;
复杂匹配规则
用正则表达式进行多行匹配时,用"^"表示开头,"$"表示结尾;
匹配指定范围:
使用[……]可以匹配到范围内的字符,如[123456789]\d或[1-9]\d,开头就不能取到0;
或规则匹配:
"|",AB|CD
使用括号
a\s(b|c|d),也就是 a\sb|a\sc|a\sd
分组匹配
简单的String。matches()方法,只能简单地匹配字符串,无法将提取出字串。可以使用Pattern类和Matcher类,提取子串。
正则表达式用(……)分组可以通过Matcher对象快速提取子串。
Pattern p = Pattern.compile("(\\d{3,4})\\-(\\d{7,8})");Matcher m = p.matcher("010-1234567");if (m.matches()){ //尝试整个序列和模式匹配System.out.println("Yes");System.out.println(m.group(0)); //返回上一个匹配操作期间匹配的子序列System.out.println(m.group(1));System.out.println(m.group(2));}else{System.out.println("匹配失败!");}
非贪婪匹配
正则表达式匹配默认使用贪婪匹配,就是从左到右进行匹配,贪婪地满足前面的,才到后面的。
可以使用"?"表示对某一规则进行非贪婪匹配,如 "(\\d+?)(0*)"
分割字符串
使用正则表达式分割字符串可以实现更加灵活的功能。String。split()方法传入的正是正则表达式。
String a="a b c";
for (String x:a.split("\\s")){
System.out.print(x+" ");
}
System.out.println();
a="a b c";
for (String x:a.split("\\s")){
System.out.print(x+" ");
}
System.out.println();
a="a, b ;; c";
for (String x:a.split("[\\,\\;\\s]+")){
System.out.print(x+" ");
}
搜索字符串
使用正则表达式可以搜索字符串
String s = "the quick brwn fox jumps over the lazy dog.";
Pattern p = Pattern.compile("\\wo\\w");
Matcher m = p.matcher(s); //创建一个匹配器对象
while (m.find()) { //查找字符串序列中与模式匹配的下一个子序列
String sub = s.substring(m.start(), m.end()); //返回上一个匹配操作期间匹配的子序列的起始和结束索引
System.out.println(sub);
}
替换字符串
使用正则表达式替换字符串可以直接调用String.replaceALL("\\s+"," ");
二、函数式编程
函数式编程
函数式面向过程的程序设计的基本单元
Java不支持单独定义函数,但是可以将静态方法视为独立函数,对象方法看作自带this参数的函数。
函数式编程就是一种抽象程度很高的编程范式。
函数式编程的特点,允许把函数本身作为参数传入另一个函数,还允许返回一个函数。
Lambda基础
函数式编程是把函数作为基本运算单元,函数可以作为变量,可以接收函数,返回函数。我们经常把支持函数式编程风格称为Lambda表达式。
Lambda表达式
Java8开始,可以用Lambda表达式替换单方法接口,大大简化代码!!
// 只需要写出方法定义,可以省略参数类型,替换之前的写法String[] s=new String[] {"apple0","orange1","banana11","Lemon1111"};Arrays.sort(s,(s1,s2)->{return s2.length()-s1.length();});System.out.println(Arrays.toString(s));
方法引用
接口和某个方法签名一致,就可以直接传入方法引用。一个接口方法和一个静态方法,除了方法名外,方法参数一致,返回类型相同,就可以说两者的方法签名一致,可以直接吧方法名作为Lambda表达式传入。
Stream
使用Stream
Java8开始,引入了一个全新的流式API:Stream API,位于java.util.stream包
这个Stream不同于java.io的Inputstream和OutputStream,它代表的是任意Java对象的序列。
Stream输出的元素可能并没有预先存储在内存中,而是实时计算出来的;是惰性计算;
List存储的每个元素都是已经存储在内存中的某个Java对象。
Stream可以看作是一个容器,数据类型。
Stream特点:
它可以存储有限个或者无限个元素,因为它是惰性计算,所以它是根据实时需要进行计算。
一个Stream可以轻易地转换成为另一个Stream,而不是修改原Stream本身,实际上只存储了转换规则,并没有任何计算发生。
Stream API 提供了一套新的流式处理的抽象序列
Stream API 支持函数式编程和链式操作
Stream 可以表示无限序列,并且大多数情况下是惰性求值的。
每个Steam只会被操作一次,就是当完成最终计算后,就会被关闭
StreamAPI的基本用法:
创建一个Stream,然后做若干次转换,最后调用一个求值方法获取真正计算的结果。
创建Stream
这两种方法都是把一个现有的序列变为Stream,它的元素是固定的
静态方法stream.of()创建Stream,但是只能传入和输出确定的元素
Stream<Integer> a = Stream.of(12, 1, 2, 4);a.forEach(System.out::print);System.out.println();//基于数组或者集合创建StreamStream<String> b = Arrays.stream(new String[]{"A", "B", "C", "D"});b.forEach(System.out::print);System.out.println();Stream<String> c = List.of("X", "Y", "Z").stream();c.forEach(System.out::print);System.out.println();
基于Supplier方法
通过Stream.generate()方法,创建Stream,需要传入一个Supplier对象
在使用forEach()或count()这些最终求值操作时,要将这个无限序列变成有限序列,可以使用limit()方法,再求最终值。
Stream<Integer> d = Stream.generate(new Natual());d.limit(10).forEach(s-> System.out.print(s+" "));System.out.println();
通过一些API提供的接口,直接获得Stream
Files类的lines()方法可以把一个文件变成一个Stream,每个元素代表文件的一行内容。
Stream<String> e = Files.lines(Paths.get("D://name"));
正则表达式的Pattern对象有一个splitAsStream()方法,可以直接把一个长字符串分割为Stream序列而不是数组。
Pattern p =Pattern.compile("\\s+");Stream<String> f = p.splitAsStream("My name is olderhard");f.forEach(System.out::println);
针对基本类型
因为Java泛型不支持基本泛型,要是使用Stream<Integer>又会产生频繁的装箱、拆箱操作。
Java标准库提供了IntStream、LongStream、DoubleStream三种使用基本类型的Stream,提供运行效率
IntStream g=Arrays.stream(new int[]{1,2,3});g.forEach(System.out::print);}
}
class Natual implements Supplier<Integer>{int a=0,b=1,c=0;@Overridepublic Integer get() {c=a+b;a=b;b=c;return a;}
}
使用map
使用Stream.map()是Stream最常用的一个转换方法,它把一个Stream转换为另一个新的Stream
map操作,把一个Stream的每个元素一一对应到应用了目标函数的结果上
Stream<Integer> a1 = Stream.of(1, 2, 3, 4, 5);//a1.forEach(System.out::print);//System.out.println();Stream<Integer> b = a1.map(n -> n * n);b.forEach(s-> System.out.print(s+" "));System.out.println();
利用map(),完成字符串的操作,以及任何Java对象都是非常有用的
List.of(" Apple ", " pear ", " ORANGE", " BaNaNa ").stream().map(String::trim) // 去空格.map(String::toLowerCase) // 变小写.forEach(System.out::println); // 打印
使用filter
Stream.filter()是Stream的另一个常用转换方法
所谓filter操作,就是对一个Stream的所有元素一一进行测试,不满足条件的删去,剩下的构成一个新的Stream。
IntStream.of(1,2,3,4,5,6,7,8,9).filter(n->n%2!=0).forEach(s-> System.out.print(s+" "));
filter()除了常用于数值外,也可应用于任何Java对象
使用reduce
Stream.reduce()是Stream的一个聚合方法,它可以把一个Stream的所有元素按照聚合函数聚合成一个结果。
聚合运算会不断请求它的上游输出它的每个元素,之后上游经过一系列的转换,最终被reduce()聚合出结果。
聚合操作是真正需要从Stream请求数据的,对一个Stream做聚合计算后,结果就不是一个Stream,而是其他的一个Java对象。
int sum= Stream.of(1,2,3,4,5,6,7,8,9,10).reduce(0,(res,n)->res+n);System.out.println(sum);
除了可以对数值进行累积计算外,还可以灵活运用reduce()对Java对象进行操作
List<String> props = List.of("profile=native", "debug=true", "logging=warn", "interval=500");Map<String, String> q = props.stream()// 把k=v转换为Map[k]=v:.map(kv -> {String[] ss = kv.split("\\=", 2);return Map.of(ss[0], ss[1]);})// 把所有Map聚合到一个Map:.reduce(new HashMap<String, String>(), (m, kv) -> {m.putAll(kv);return m;});// 打印结果:q.forEach((k, v) -> System.out.println(k + " = " + v));}
输出集合
将Stream变为List或者其他都是个聚合操作,它会强制Stream输出每个元素。
输出为List
将Stream中每个元素收集到List,使用filter方法过滤,使用collector方法收集
Stream<String> stream = Stream.of("apple", "", null, "pear", " ", "orange");List<String> collect = stream.filter(s -> s != null && !s.isBlank()).collect(Collectors.toList());System.out.println(collect);
输出为数组
Stream<String> stream1 = Stream.of("apple", "", null, "pear", " ", "orange").filter(s -> s != null && !s.isBlank());String [] a=stream1.toArray(String[]::new);System.out.println(Arrays.toString(a));
输出为Map
Stream<String> stream2 = Stream.of("APPL:Apple", "MSFT:Microsoft");Map <String,String> map = stream2.collect(Collectors.toMap(s->s.substring(0,s.indexOf(':')), s->s.substring(s.indexOf(':')+1)));System.out.println(map);
分组输出
List<String> list = List.of("Apple", "Banana", "Blackberry", "Coconut", "Avocado", "Cherry", "Apricots");Map<String, List<String>> groups = list.stream().collect(Collectors.groupingBy(s -> s.substring(0, 1), Collectors.toList()));System.out.println(groups);
其他操作
排序
对Stream的元素进行排序List<String> l = List.of("orange", "apple", "Banana").stream().sorted().collect(Collectors.toList());System.out.println(l);
去重
对一个Stream的元素进行去重
List<String> l1 = List.of("orange", "orange", "Banana").stream().distinct().collect(Collectors.toList());System.out.println(l1);
截取
把一个无限的Stream转换成有限的Stream,skip()用于跳过当前Stream的前N个元素,limit()用于截取当前Stream最多N个元素。
List<String> l2 = List.of("orange", "apple", "Banana","pear","water").stream().skip(1).limit(3).collect(Collectors.toList());System.out.println(l2);
合并
将两个Stream合并为一个Stream可以使用Stream的静态方法concat()
Stream<Integer> stream1 = List.of(1, 2, 3).stream();Stream<Integer> stream2 = List.of(4, 5, 6).stream();Stream<Integer> concat = Stream.concat(stream1, stream2);System.out.println(concat.collect(Collectors.toList()));
其他一些方法就不细说了