Java源码分析 | CharSequence
本文基于 OracleJDK 11, HotSpot 虚拟机。
CharSequence 定义
CharSequence 是 java.lang 包下的一个接口,是 char 值的可读序列, 即其本身描述的就是字符串。因此我们可以直接使用如下:
CharSequence cs1 = "朝雾轻寒"; // 默认实现为 String
CharSequence cs2 = new StringBuilder("朝雾轻寒");
CharSequence cs3 = new StringBuffer("朝雾轻寒");
扩展
char 数据类型是基于原始 Unicode 规范,Unicode 标准定义了合法代码点(code point)的范围是 U+0000 到 U+10FFFF,称为 Unicode scalar value。其中从 U+0000 到 U+FFFF 的字符集称为基本多语言平面 (BMP),码位大于 U+FFFF 的字符集称为补充平面。
一个 char 值表示基本多语言平面(BMP)代码点,可以是代理代码点或 UTF-16 编码的代码单元。一个
int值表示所有 Unicode 代码点,包括补充代码点。低(最低有效)21 位int用于表示 Unicode 代码点,高(最高)11 位必须为零。只接受
char值的方法不支持补充字符。
CharSequence 提供了对多种不同类型的 char 序列的统一只读访问,它有如下的实现类,如 String、StringBuffer、StringBuilder、CharBuffer 类等。

此接口不修改equals 和hashCode 方法的常规协定。因此,通常未定义比较实现 CharSequence 的两个对象的结果。每个对象都可以通过一个不同的类实现,而且不能保证每个类能够测试其实例与其他类的实例的相等性。因此,使用任意 CharSequence 实例作为集合中的元素或映射中的键是不合适的。
方法
length()
该方法的作用是返回此字符序列的长度。长度是序列中 16 位 char 的个数。
int length();
charAt(int index)
该方法的作用是返回指定索引处的 char 值。索引范围从零到length() - 1,与数组的索引一样。如果char索引指定的值是 surrogate,则返回代理值。
char charAt(int index);
注意:如果 index 超出索引范围,则会抛出 IndexOutOfBoundsException异常。
subSequence(int start, int end)
该方法的作用是返回一个 CharSequence 序列的子序列。该子序列以char指定索引处的值开始,以索引处的char值结束end - 1。返回序列的长度(以char为单位)为end - start,因此如果start == end 返回一个空序列。
CharSequence subSequence(int start, int end);
注意: 如果 start 或 end 超出索引范围,或者 start大于end,则会抛出 IndexOutOfBoundsException异常。
toString()
该方法是返回以与此序列相同的顺序返回包含此序列中字符的字符串。字符串的长度就是这个序列的长度。
public String toString();
这是重写了 Object 方法, Object 类不需要显示的继承。
chars()
该方法在 JDK1.8 中加入,其作用是返回此序列中int值的零扩展流。char任何映射到代理代码点的字符都会未经解释地传递。如果在读取流时发生了突变,则结果是未定义的。
public default IntStream chars() {
// 实现了int原语迭代器
class CharIterator implements PrimitiveIterator.OfInt {
int cur = 0;
// 如果迭代器中还有元素,则返回true。
public boolean hasNext() {
return cur < length();
}
// 返回迭代器中的下一个元素
public int nextInt() {
if (hasNext()) {
return charAt(cur++);
} else {
throw new NoSuchElementException();
}
}
/* 对每个剩余元素执行指定的操作,直到所有元素。
已被处理或动作引发异常。行动是
按照迭代顺序执行,如果指定了该顺序。
由操作引发的异常会传递给调用者。*/
@Override
public void forEachRemaining(IntConsumer block) {
for (; cur < length(); cur++) {
block.accept(charAt(cur));
}
}
}
// 返回一个通过继承了 Spliterator.OfInt 的 Supplier 创建的新的顺序或并行 IntStream
return StreamSupport.intStream(() ->
// 创建一个拆分器,以 CharIterator 作为元素的源,同时报告初始元素数量,源或者元素的特征
Spliterators.spliterator(
new CharIterator(),
length(),
Spliterator.ORDERED),
Spliterator.SUBSIZED | Spliterator.SIZED | Spliterator.ORDERED,
false);
}
很多情况下,我们可以直接使用包装类如 Integer 直接进行数据处理,得益于 JDK1.5 中添加了自动装箱功能。但如果在内部循环中进行装箱或拆箱操作, 会带来大量 CPU 和垃圾回收机制的开销。
在 JDK1.8 中我们知道加入了 Stream 流支持,在设计 api 时充分考虑对原语支持,综合性能、开销、维护成本等,添加了对 int、long、double 的原语专门化,详细可见:传送门。
codePoints()
该方法在 JDK1.8 中加入,其作用是返回此序列的一个代码点值流。在序列中遇到的任何代理对都是按字符组合的。 toCodePoint 和结果被传递到流。任何其他代码单元,包括普通的BMP字符、未配对的代理和未定义的代码单元,都是零扩展到int值,然后将这些值传递给流。 如果在读取流时发生了突变,则结果是未定义的。
public default IntStream codePoints() {
class CodePointIterator implements PrimitiveIterator.OfInt {
int cur = 0;
/* 对每个剩余元素执行指定的操作,直到所有元素。
已被处理或动作引发异常。行动是
按照迭代顺序执行,如果指定了该顺序。
由操作引发的异常会传递给调用者。*/
@Override
public void forEachRemaining(IntConsumer block) {
final int length = length();
int i = cur;
try {
while (i < length) {
char c1 = charAt(i++);
if (!Character.isHighSurrogate(c1) || i >= length) {
block.accept(c1);
} else {
char c2 = charAt(i);
if (Character.isLowSurrogate(c2)) {
i++;
block.accept(Character.toCodePoint(c1, c2));
} else {
block.accept(c1);
}
}
}
} finally {
cur = i;
}
}
// 如果迭代器中还有元素,则返回true
public boolean hasNext() {
return cur < length();
}
// 返回迭代器中的下一个元素
public int nextInt() {
final int length = length();
if (cur >= length) {
throw new NoSuchElementException();
}
char c1 = charAt(cur++);
if (Character.isHighSurrogate(c1) && cur < length) {
char c2 = charAt(cur);
if (Character.isLowSurrogate(c2)) {
cur++;
return Character.toCodePoint(c1, c2);
}
}
return c1;
}
}
// 返回一个通过继承了 Spliterator.OfInt 的 Supplier 创建的新的顺序或并行IntStream
return StreamSupport.intStream(() ->
Spliterators.spliteratorUnknownSize(
new CodePointIterator(),
Spliterator.ORDERED),
Spliterator.ORDERED,
false);
}
compare(CharSequence cs1, CharSequence cs2)
该方法在 JDK11 中加入,其作用是按字典顺序比较两个CharSequence实例。如果第一个序列按字典顺序分别小于、等于或大于第二个,则返回负值、零或正值。
public static int compare(CharSequence cs1, CharSequence cs2) {
if (Objects.requireNonNull(cs1) == Objects.requireNonNull(cs2)) {
return 0;
}
if (cs1.getClass() == cs2.getClass() && cs1 instanceof Comparable) {
return ((Comparable<Object>) cs1).compareTo(cs2);
}
for (int i = 0, len = Math.min(cs1.length(), cs2.length()); i < len; i++) {
char a = cs1.charAt(i);
char b = cs2.charAt(i);
if (a != b) {
return a - b;
}
}
return cs1.length() - cs2.length();
}
将长度为len的CharSequence cs视为 char 值的序列,从cs[0]到cs[len-1]。假设 k 是每个序列中对应的 char 值不同的最低索引。序列的字典顺序由 char 值cs1[k]与cs2[k]的数值比较确定。如果没有这样的索引k,则较短的序列在字典上被认为小于另一个。如果序列具有相同的长度,则认为这些序列在字典上是相等的。
更多源码分析
Java源码分析 | CharSequence的更多相关文章
- Java源码分析:关于 HashMap 1.8 的重大更新(转载)
http://blog.csdn.net/carson_ho/article/details/79373134 前言 HashMap 在 Java 和 Android 开发中非常常见 而HashMap ...
- JAVA源码分析-HashMap源码分析(二)
本文继续分析HashMap的源码.本文的重点是resize()方法和HashMap中其他的一些方法,希望各位提出宝贵的意见. 话不多说,咱们上源码. final Node<K,V>[] r ...
- Java源码分析之LinkedList
LinkedList与ArrayList正好相对,同样是List的实现类,都有增删改查等方法,但是实现方法跟后者有很大的区别. 先归纳一下LinkedList包含的API 1.构造函数: ①Linke ...
- Java源码分析:Guava之不可变集合ImmutableMap的源码分析
一.案例场景 遇到过这样的场景,在定义一个static修饰的Map时,使用了大量的put()方法赋值,就类似这样-- public static final Map<String,String& ...
- JAVA源码分析-HashMap源码分析(一)
一直以来,HashMap就是Java面试过程中的常客,不管是刚毕业的,还是工作了好多年的同学,在Java面试过程中,经常会被问到HashMap相关的一些问题,而且每次面试都被问到一些自己平时没有注意的 ...
- 【转】【java源码分析】Map中的hash算法分析
全网把Map中的hash()分析的最透彻的文章,别无二家. 2018年05月09日 09:08:08 阅读数:957 你知道HashMap中hash方法的具体实现吗?你知道HashTable.Conc ...
- 【Java源码分析】LinkedList类
LinkedList<E> 源码解读 继承AbstractSequentialList<E> 实现List<E>, Deque<E>, Cloneabl ...
- 一致性哈希Java源码分析
首次接触一致性哈希是在学习memcached的时候,为了解决分布式服务器的负载均衡或者说选路的问题,一致性哈希算法不仅能够使memcached服务器被选中的概率(数据分布)更加均匀,而且使得服务器的增 ...
- JAVA源码分析------锁(1)
http://870604904.iteye.com/blog/2258604 第一次写博客,也就是记录一些自己对于JAVA的一些理解,不足之处,请大家指出,一起探讨. 这篇博文我打算说一下JAVA中 ...
随机推荐
- Koa系框架(egg/cabloy)如何获取微信支付回调请求中的xml参数
背景 在Koa系框架(如EggJS)中进行微信支付开发时,遇到一个问题:微信支付平台会发送一个回调请求,通知支付订单的处理结果.该请求传入的参数是xml格式,而Koa中间件koa-bodyparser ...
- Docker容器编译安装Nginx
Docker容器编译安装Nginx,最简单的Nginx配置. 创建容器&进入容器 宿主机2080映射容器的80端口 [root@localhost ~]# docker run -i -d - ...
- 10分钟快速部署camunda BPM开源版
安装部署Camunda BPM有多种方式,基于Camunda独立web应用程序安装部署是最简单的一种方式,您只需要有tomcat即可. 本文档将指导您安装和配置Camunda独立web应用程序,快速体 ...
- Tomcat部署界面使用burp爆破
打开界面显示私密连接,正常抓包. 抓包查看Authorization的数据 Basic 后面的数据是经过base64加密后的.格式为admin:123456 勾选对应参数,payload设置为Cust ...
- 使用aggregation API扩展你的kubernetes API
Overview What is Kubernetes aggregation Kubernetes apiserver aggregation AA 是Kubernetes提供的一种扩展API的方法 ...
- 知道vue组件同级传值吗?
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- sql-DCL用户及权限管理及其他常用命令-mysql
查看系统信息 SHOW PROCESSLIST -- 显示哪些线程正在运行 SHOW VARIABLES -- 显示系统变量信息 SELECT now(), user(), version(); -- ...
- Python自动化办公:批量将文件按分类保存,文件再多,只需一秒钟解决
序言 (https://jq.qq.com/?_wv=1027&k=GmeRhIX0) 当我们电脑里面的文本或者或者文件夹太多了,有时候想找到自己想要的文件,只能通过去搜索文件名,要是名字忘记 ...
- 【RocketMQ】消息的存储
Broker对消息的处理 BrokerController初始化的过程中,调用registerProcessor方法注册了处理器,在注册处理器的代码中可以看到创建了处理消息发送的处理器对象SendMe ...
- 一题多解,ASP.NET Core应用启动初始化的N种方案[上篇]
ASP.NET Core应用本质上就是一个由中间件构成的管道,承载系统将应用承载于一个托管进程中运行起来,其核心任务就是将这个管道构建起来.在ASP.NET Core的发展历史上先后出现了三种应用承载 ...