Java ソースコード分析 | CharSequence

この記事は、HotSpot 仮想マシンである OracleJDK 11 に基づいています。

CharSequence 定義

CharSequencechar 値の読み取り可能なシーケンスであるjava.langpackageです。つまり、それ自体で文字列を記述します。したがって、次のように直接使用できます。

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

拡大

char データ型は、 Unicode スカラー値と呼ばれる U+0000 から U+10FFFF の範囲の有効なコード ポイントを定義する元の Unicode 仕様に基づいていますU+0000 から U+FFFF までの文字セットはBasic Multilingual Plane (BMP)と呼ばれ、コード ポイントが U+FFFF より大きい文字セットは Supplementary Plane と呼ばれます。

サロゲート コード ポイントまたは UTF-16 でエンコードされたコード単位のいずれかである Basic Multilingual Plane (BMP) コード ポイントを表す char 値。intは、補助コード ポイントを含むすべての Unicode コード ポイントを表します。下位 (最下位) 21 ビットはintUnicode コード ポイントを表すために使用され、上位 (最上位) 11 ビットは 0 でなければなりません。

値のみのcharメソッドは補助文字をサポートしていません。

CharSequenceさまざまな種類の文字シーケンスへの統一された読み取り専用アクセスを提供します。 StringStringBufferStringBuilderCharBufferクラスなどの次の実装クラスがあります。

image-20220823114718245

equalsこのインターフェイスは、およびメソッドhashCodeの一般契約を変更しません。したがって、CharSequence実装の 2 つのオブジェクトを比較した結果は、です。各オブジェクトは異なるクラスによって実装される可能性があり、各クラスがそのインスタンスが他のクラスのインスタンスと等しいかどうかをテストできるという保証はありません。したがって、任意のCharSequenceインスタンス。

方法

長さ()

该方法的作用是返回此字符序列的长度。长度是序列中 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,则较短的序列在字典上被认为小于另一个。如果序列具有相同的长度,则认为这些序列在字典上是相等的。

更多源码分析

おすすめ

転載: juejin.im/post/7135745300936785951