我正在为我工作的公司准备正则表达式教程更新。 原始教程创建于2012年,从那时起Java发生了一点变化。 有新的Java语言版本,尽管Java中的正则表达式处理仍不完善(nb。它仍使用非确定性FSA),但仍有一些新功能。 我在上一篇文章中针对新的Java 9方法写过一些文章。 但是这一次,我必须查看自2012年以来的所有新功能。
从1.8开始的splitAsStream
这样,我在java.util.regex.Pattern
类中找到了splitAsStream
。 它与方法split
几乎相同,除了返回的不是String
对象的数组而是流。 最简单的实现是
public Stream<String> splitAsStream(final CharSequence input) {
return Arrays.stream(p.split(input));
}
当图书馆试图跟上新风和支持潮流时,我可以看到许多这样的实现。 没有什么比将数组或列表从某些现有功能转换为流更简单了。
但是,解决方案是低于标准,失去了流的本质:仅执行所需的工作。 我的意思是,“应仅在处理流时进行所需的工作”,而不是在开发人员将数组或集合返回方法转换为返回一个流的情况下进行。 流以一种精简的方式及时交付结果。 您会看到我们有多少个懒惰的表情。
JDK实现利用了流的性能优势。 如果您查看源代码,则可以立即看到实现比前面提到的简单解决方案稍微复杂一些。 由于我没有时间去研究实现,也许没有兴趣,因此我使用了另一种方法来证明实现尊重流的惰性。
该方法的参数是CharSequence
而不是String
。 CharSequence
是String
实现的接口,但我们也可以实现它。 为了感觉到这种情况下的流实现多么懒惰,我创建了一个CharSequence
实现,它通过调试打印出了方法调用。
class MyCharSequence implements CharSequence {
private String me;
MyCharSequence(String me) {
this.me = me;
}
@Override
public int length() {
System.out.println("MCS.length()=" + me.length());
return me.length();
}
@Override
public char charAt(int index) {
System.out.println("MCS.charAt(" + index + ")=" + me.charAt(index));
return me.charAt(index);
}
@Override
public CharSequence subSequence(int start, int end) {
System.out.println("MCS.subSequence(" + start + "," + end + ")="
+ me.subSequence(start, end));
return me.subSequence(start, end);
}
}
有了此类,我可以执行以下简单的main方法:
public static void main(String[] args) {
Pattern p = Pattern.compile("[,\\.\\-;]");
final CharSequence splitIt =
new MyCharSequence("one.two-three,four;five;");
p.splitAsStream(splitIt).forEach(System.out::println);
}
输出显示该实现确实很懒:
MCS.length()=24
MCS.length()=24
MCS.length()=24
MCS.charAt(0)=o
MCS.charAt(1)=n
MCS.charAt(2)=e
MCS.charAt(3)=.
MCS.subSequence(0,3)=one
one
MCS.length()=24
MCS.charAt(4)=t
MCS.charAt(5)=w
MCS.charAt(6)=o
MCS.charAt(7)=-
MCS.subSequence(4,7)=two
two
MCS.length()=24
MCS.charAt(8)=t
MCS.charAt(9)=h
MCS.charAt(10)=r
MCS.charAt(11)=e
MCS.charAt(12)=e
MCS.charAt(13)=,
MCS.subSequence(8,13)=three
three
MCS.length()=24
MCS.charAt(14)=f
MCS.charAt(15)=o
MCS.charAt(16)=u
MCS.charAt(17)=r
MCS.charAt(18)=;
MCS.subSequence(14,18)=four
four
MCS.length()=24
MCS.charAt(19)=f
MCS.charAt(20)=i
MCS.charAt(21)=v
MCS.charAt(22)=e
MCS.charAt(23)=;
MCS.subSequence(19,23)=five
five
MCS.length()=24
实现继续进行,当它找到流的第一个元素时,将其返回。 我们可以处理字符串“ one”,并且只有在返回其他元素时,它才能处理其他字符。 为什么必须在开始时调用方法长度3次? 我不知道。 也许它想非常确定序列的长度不会发生神奇的变化。
士气
这是一个很好的例子,说明如何扩展库以支持流。 如果应用程序仅将集合或数组转换为第一个版本中的流,这不是问题,但是如果分析表明性能可以收回投资,则应实现真正的流惰性。
边注
CharSequence
的实现是可变的,但是处理要求它保持恒定,否则结果不确定。 我可以确认。
下周,我将展示splitAsStream
一种可能用法,该splitAsStream
利用的功能是它在字符序列中的读入超出了需要的范围。
翻译自: https://www.javacodegeeks.com/2017/11/split-as-stream.html