Java source code analysis | CharSequence

This article is based on OracleJDK 11, the HotSpot virtual machine.

CharSequence Definition

CharSequenceIs an interface under the java.langpackage , which is a readable sequence of char values, that is, it describes a string by itself. So we can use it directly as follows:

CharSequence cs1 = "朝雾轻寒";  // 默认实现为 String
CharSequence cs2 = new StringBuilder("朝雾轻寒");
CharSequence cs3 = new StringBuffer("朝雾轻寒");
复制代码

expand

The char data type is based on the original Unicode specification, which defines a legal code point in the range of U+0000 to U+10FFFF, called a Unicode scalar value . The character set from U+0000 to U+FFFF is called Basic Multilingual Plane (BMP) , and the character set whose code point is greater than U+FFFF is called Supplementary Plane.

A char value representing the Basic Multilingual Plane (BMP) code point, either a surrogate code point or a UTF-16 encoded code unit. A intvalue represents all Unicode code points, including supplementary code points. The low (least significant) 21 bits are intused to represent Unicode code points, and the high (highest) 11 bits must be zero.

Value- only charmethods do not support supplementary characters.

CharSequenceProvides unified read-only access to a variety of different types of char sequences, it has the following implementation classes, such as String, StringBuffer, StringBuilder, CharBufferclass, etc.

image-20220823114718245

This interface does not modify the general contract of the equalsand methods. hashCodeTherefore, the result CharSequenceof comparing two objects of an implementation is . Each object can be implemented by a different class, and there is no guarantee that each class will be able to test its instances for equality with instances of other classes. Therefore, it is not appropriate to use an arbitrary CharSequenceinstance as an element in a collection or as a key in a map.

method

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);
复制代码

注意: 如果 startend 超出索引范围,或者 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();
}
复制代码

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

更多源码分析

Guess you like

Origin juejin.im/post/7135745300936785951