• 分析Java中 CharSequence源码


    CharSequence 定义

    CharSequence 是 java.lang 包下的一个接口,是 char 值的可读序列, 即其本身描述的就是字符串。因此我们可以直接使用如下:

    1. <pre class="prettyprint hljs dart" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">CharSequence cs1 = "朝雾轻寒"; // 默认实现为 String
    2. CharSequence cs2 = new StringBuilder("朝雾轻寒");
    3. 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 的个数。

    <pre class="hljs cpp" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">int length();
    

    charAt(int index)

    该方法的作用是返回指定索引处的 char 值。索引范围从零到 length() - 1 ,与数组的索引一样。如果 char 索引指定的值是 surrogate,则返回代理值。

    <pre class="hljs cpp" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">char charAt(int index);
    

    注意:如果 index 超出索引范围,则会抛出 IndexOutOfBoundsException 异常。

    subSequence(int start, int end)

    该方法的作用是返回一个 CharSequence 序列的子序列。该子序列以 char 指定索引处的值开始,以索引处的 char 值结束 end - 1 。返回序列的长度(以 char 为单位)为 end - start ,因此如果 start == end 返回一个空序列。

    <pre class="hljs sql" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">CharSequence subSequence(int start, int end);
    

    注意:如果 start 或 end 超出索引范围,或者 start 大于 end ,则会抛出 IndexOutOfBoundsException 异常。

    toString()

    该方法是返回以与此序列相同的顺序返回包含此序列中字符的字符串。字符串的长度就是这个序列的长度。

    <pre class="hljs delphi" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">public String toString();
    

    这是重写了 Object 方法, Object 类不需要显示的继承。

    chars()

    该方法在 JDK1.8 中加入,其作用是返回此序列中 int 值的零扩展流。 char 任何映射到 代理代码点 的字符都会未经解释地传递。如果在读取流时发生了突变,则结果是未定义的。

    1. <pre class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">public default IntStream chars() {
    2. // 实现了int原语迭代器
    3. class CharIterator implements PrimitiveIterator.OfInt {
    4. int cur = 0;
    5. // 如果迭代器中还有元素,则返回true
    6. public boolean hasNext() {
    7. return cur < length();
    8. }
    9. // 返回迭代器中的下一个元素
    10. public int nextInt() {
    11. if (hasNext()) {
    12. return charAt(cur++);
    13. } else {
    14. throw new NoSuchElementException();
    15. }
    16. }
    17. /* 对每个剩余元素执行指定的操作,直到所有元素。
    18. 已被处理或动作引发异常。行动是
    19. 按照迭代顺序执行,如果指定了该顺序。
    20. 由操作引发的异常会传递给调用者。*/
    21. @Override
    22. public void forEachRemaining(IntConsumer block) {
    23. for (; cur < length(); cur++) {
    24. block.accept(charAt(cur));
    25. }
    26. }
    27. }
    28. // 返回一个通过继承了 Spliterator.OfInt 的 Supplier 创建的新的顺序或并行 IntStream
    29. return StreamSupport.intStream(() ->
    30. // 创建一个拆分器,以 CharIterator 作为元素的源,同时报告初始元素数量,源或者元素的特征
    31. Spliterators.spliterator(
    32. new CharIterator(),
    33. length(),
    34. Spliterator.ORDERED),
    35. Spliterator.SUBSIZED | Spliterator.SIZED | Spliterator.ORDERED,
    36. false);
    37. }

    很多情况下,我们可以直接使用包装类如 Integer 直接进行数据处理,得益于 JDK1.5 中添加了自动装箱功能。但如果在内部循环中进行装箱或拆箱操作, 会带来大量 CPU 和垃圾回收机制的开销。

    在 JDK1.8 中我们知道加入了 Stream 流支持,在设计 api 时充分考虑对原语支持,综合性能、开销、维护成本等,添加了对 int、long、double 的原语专门化.

    codePoints()

    该方法在 JDK1.8 中加入,其作用是返回此序列的一个代码点值流。在序列中遇到的任何代理对都是按字符组合的。 toCodePoint 和结果被传递到流。任何其他代码单元,包括普通的BMP字符、未配对的代理和未定义的代码单元,都是零扩展到int值,然后将这些值传递给流。 如果在读取流时发生了突变,则结果是未定义的。

    1. <pre class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">public default IntStream codePoints() {
    2. class CodePointIterator implements PrimitiveIterator.OfInt {
    3. int cur = 0;
    4. /* 对每个剩余元素执行指定的操作,直到所有元素。
    5. 已被处理或动作引发异常。行动是
    6. 按照迭代顺序执行,如果指定了该顺序。
    7. 由操作引发的异常会传递给调用者。*/
    8. @Override
    9. public void forEachRemaining(IntConsumer block) {
    10. final int length = length();
    11. int i = cur;
    12. try {
    13. while (i < length) {
    14. char c1 = charAt(i++);
    15. if (!Character.isHighSurrogate(c1) || i >= length) {
    16. block.accept(c1);
    17. } else {
    18. char c2 = charAt(i);
    19. if (Character.isLowSurrogate(c2)) {
    20. i++;
    21. block.accept(Character.toCodePoint(c1, c2));
    22. } else {
    23. block.accept(c1);
    24. }
    25. }
    26. }
    27. } finally {
    28. cur = i;
    29. }
    30. }
    31. // 如果迭代器中还有元素,则返回true
    32. public boolean hasNext() {
    33. return cur < length();
    34. }
    35. // 返回迭代器中的下一个元素
    36. public int nextInt() {
    37. final int length = length();
    38. if (cur >= length) {
    39. throw new NoSuchElementException();
    40. }
    41. char c1 = charAt(cur++);
    42. if (Character.isHighSurrogate(c1) && cur < length) {
    43. char c2 = charAt(cur);
    44. if (Character.isLowSurrogate(c2)) {
    45. cur++;
    46. return Character.toCodePoint(c1, c2);
    47. }
    48. }
    49. return c1;
    50. }
    51. }
    52. // 返回一个通过继承了 Spliterator.OfInt 的 Supplier 创建的新的顺序或并行IntStream
    53. return StreamSupport.intStream(() ->
    54. Spliterators.spliteratorUnknownSize(
    55. new CodePointIterator(),
    56. Spliterator.ORDERED),
    57. Spliterator.ORDERED,
    58. false);
    59. }

    compare(CharSequence cs1, CharSequence cs2)

    该方法在 JDK11 中加入,其作用是按字典顺序比较两个 CharSequence 实例。如果第一个序列按字典顺序分别小于、等于或大于第二个,则返回负值、零或正值。

    1. <pre class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">public static int compare(CharSequence cs1, CharSequence cs2) {
    2. if (Objects.requireNonNull(cs1) == Objects.requireNonNull(cs2)) {
    3. return 0;
    4. }
    5. if (cs1.getClass() == cs2.getClass() && cs1 instanceof Comparable) {
    6. return ((Comparable<Object>) cs1).compareTo(cs2);
    7. }
    8. for (int i = 0, len = Math.min(cs1.length(), cs2.length()); i < len; i++) {
    9. char a = cs1.charAt(i);
    10. char b = cs2.charAt(i);
    11. if (a != b) {
    12. return a - b;
    13. }
    14. }
    15. return cs1.length() - cs2.length();
    16. }

    将长度为 len 的 CharSequence cs 视为 char 值的序列,从 cs[0] 到 cs[len-1] 。假设 k 是每个序列中对应的 char 值不同的最低索引。序列的字典顺序由 char 值 cs1[k] 与 cs2[k] 的数值比较确定。如果没有这样的索引 k ,则较短的序列在字典上被认为小于另一个。如果序列具有相同的长度,则认为这些序列在字典上是相等的。

  • 相关阅读:
    九州云与英特尔联合发布智慧校园私有云框架,赋能教育新基建
    极端气候?自然灾害?【实战】机器学习预测森林火灾
    C++折半查找具体介绍及用法
    Spring更简单的读取和存储
    LeetCode-99. Recover Binary Search Tree [C++][Java]
    【考研】数据结构考点——直接选择排序
    广电运通面试
    【数据库】哪些操作会导致索引失效
    第三章 使用管理门户(三)
    无人机中的坐标系与相机姿态计算
  • 原文地址:https://blog.csdn.net/Java_ttcd/article/details/126541543