Análisis de código fuente de Java | CharSequence

Este artículo se basa en OracleJDK 11, la máquina virtual HotSpot.

Definición de secuencia de caracteres

CharSequenceEs una interfaz bajo el java.langpaquete , que es una secuencia legible de valores char, es decir, describe una cadena por sí misma. Entonces podemos usarlo directamente de la siguiente manera:

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

expandir

El tipo de datos char se basa en la especificación Unicode original, que define un punto de código legal en el rango de U+0000 a U+10FFFF, denominado valor escalar Unicode . El conjunto de caracteres de U+0000 a U+FFFF se denomina plano multilingüe básico (BMP) , y el conjunto de caracteres cuyo punto de código es mayor que U+FFFF se denomina plano suplementario.

Un valor de carácter que representa el punto de código del plano multilingüe básico (BMP), ya sea un punto de código sustituto o una unidad de código codificada en UTF-16. Un intvalor representa todos los puntos de código Unicode, incluidos los puntos de código complementarios. Los 21 bits bajos (menos significativos) se intutilizan para representar puntos de código Unicode, y los 11 bits altos (más altos) deben ser cero.

Los métodos de solo valor charno admiten caracteres complementarios.

CharSequenceProporciona acceso unificado de solo lectura a una variedad de diferentes tipos de secuencias de caracteres, tiene las siguientes clases de implementación, como String, StringBuffer, StringBuilder, CharBufferclass, etc.

image-20220823114718245

Esta interfaz no modifica el contrato general de los métodos equalsy . hashCodePor lo tanto, el resultado CharSequencede comparar dos objetos de una implementación generalmente no está . Cada objeto puede ser implementado por una clase diferente, y no hay garantía de que cada clase pueda probar la igualdad de sus instancias con las instancias de otras clases. Por lo tanto, no es apropiado utilizar una CharSequenceinstancia como elemento de una colección o como clave en un mapa.

método

longitud()

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

更多源码分析

Supongo que te gusta

Origin juejin.im/post/7135745300936785951
Recomendado
Clasificación