字符串源码

String类的声明

// final不可被继承
public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
}
  • 比较字符串内容
public boolean equals(Object anObject) {
    // 检查是否是同一个对象的引用,如果是,直接返回 true
    if (this == anObject) {
        return true;
    }
    // 检查 anObject 是否是 String 类的实例
    if (anObject instanceof String) {
        // 将 anObject 强制转换为 String 类型
        String anotherString = (String)anObject;
        // 获取当前字符串的长度
        int n = value.length;
        // 检查两个字符串长度是否相等
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            // 遍历比较两个字符串的每个字符
            while (n-- != 0) {
                // 如果在任何位置字符不同,则返回 false
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            // 所有字符都相同,返回 true
            return true;
        }
    }
    // 如果 anObject 不是 String 类型或长度不等,则返回 false
    return false;
}

char 数组优化为 byte 数组

  • Java 9 以前,String 是用 char 型数组实现的,之后改成了 byte 型数组实现,并增加了 coder 来表示编码。这样做的好处是在 Latin1 字符(一种单字节字符集)为主的程序里,可以把 String 占用的内存减少一半。

  • char[]byte[],最主要的目的是节省字符串占用的内存空间。内存占用减少带来的另外一个好处,就是 GC 次数也会减少。

  • JDK11中String类源码

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    @Stable
    private final byte[] value;
    private final byte coder;
    private int hash;
}

31倍哈希法

public int hashCode() {
    // 从缓存中获取哈希码
    int h = hash;
    // 如果哈希码未被计算过(即为 0)且字符串不为空,则计算哈希码
    if (h == 0 && value.length > 0) {
        // 获取字符串的字符数组
        char val[] = value;
        // 遍历字符串的每个字符来计算哈希码
        for (int i = 0; i < value.length; i++) {
            // 31 倍哈希法: 使用 31 作为乘法因子
            h = 31 * h + val[i];
        }
        // 缓存计算后的哈希码
        hash = h;
    }
    // 返回哈希码
    return h;
}

subString()

  • 截取字符串
public String substring(int beginIndex) {
    // 检查起始索引是否小于 0,如果是,则抛出 StringIndexOutOfBoundsException 异常
    if (beginIndex < 0) {
        throw new StringIndexOutOfBoundsException(beginIndex);
    }
    // 计算子字符串的长度
    int subLen = value.length - beginIndex;
    // 检查子字符串长度是否为负数,如果是,则抛出 StringIndexOutOfBoundsException 异常
    if (subLen < 0) {
        throw new StringIndexOutOfBoundsException(subLen);
    }
    // 如果起始索引为 0,则返回原字符串;否则,创建并返回新的字符串
    return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
}

indexOf()

  • 查找一个子字符串在原字符串中第一次出现的位置,并返回该位置的索引
static int indexOf(char[] source, int sourceOffset, int sourceCount,
        char[] target, int targetOffset, int targetCount,
        int fromIndex) {
    // 如果开始搜索的位置已经超出 source 数组的范围,则直接返回-1(如果 target 数组为空,则返回 sourceCount)
    if (fromIndex >= sourceCount) {
        return (targetCount == 0 ? sourceCount : -1);
    }
    // 如果开始搜索的位置小于0,则从0开始搜索
    if (fromIndex < 0) {
        fromIndex = 0;
    }
    // 如果 target 数组为空,则直接返回开始搜索的位置
    if (targetCount == 0) {
        return fromIndex;
    }

    // 查找 target 数组的第一个字符在 source 数组中的位置
    char first = target[targetOffset];
    int max = sourceOffset + (sourceCount - targetCount);

    for (int i = sourceOffset + fromIndex; i <= max; i++) {
        // 如果 source 数组中当前位置的字符不是 target 数组的第一个字符,则在 source 数组中继续查找 target 数组的第一个字符
        /* Look for first character. */
        if (source[i] != first) {
            while (++i <= max && source[i] != first);
        }

        // 如果在 source 数组中找到了 target 数组的第一个字符,则继续查找 target 数组的剩余部分是否匹配
        /* Found first character, now look at the rest of v2 */
        if (i <= max) {
            int j = i + 1;
            int end = j + targetCount - 1;
            for (int k = targetOffset + 1; j < end && source[j]
                    == target[k]; j++, k++);

            // 如果 target 数组全部匹配,则返回在 source 数组中的位置索引
            if (j == end) {
                /* Found whole string. */
                return i - sourceOffset;
            }
        }
    }
    // 没有找到 target 数组,则返回-1
    return -1;
}

trim()

  • 去除字符串两侧的空白字符
public String trim() {
    int len = value.length;
    int st = 0;
    char[] val = value;    /* avoid getfield opcode */

    // 越过左边的空格
    while ((st < len) && (val[st] <= ' ')) {
        st++;
    }
    // 越过右边的空格
    while ((st < len) && (val[len - 1] <= ' ')) {
        len--;
    }
    return ((st > 0) || (len < value.length)) ? substring(st, len) : this;
}

字符串不可变

  • 可以保证 String 对象的安全性,避免被篡改

  • 保证哈希值不会频繁变更

  • 可以实现字符串常量池,Java 会将相同内容的字符串存储在字符串常量池中。这样,具有相同内容的字符串变量可以指向同一个 String 对象,节省内存空间

  • 不管是截取substring()、拼接concat(),还是替换replace(),都不是在原有的字符串上进行的,而是重新生成了新的字符串对象。也就是说,这些操作执行过后,原来的字符串对象并没有发生改变

posted @ 2024-07-14 12:19  n1ce2cv  阅读(7)  评论(0编辑  收藏  举报