String类源码 学习与分析

掌握与分析String类中常见的方法(openjdk-1.8):

  • length、charAt、compareTo、compareToIgnoreCase、concat、contains
  • startsWith、equals、indexOf、split
  • tocharArray、toLowerCase、trim、valueOf

String底层:jdk1.8及以前使用是char[ ],1.9开始使用byte[ ];

ps:Java 9引入了Compact Strings来取代Java 6的Compressed Strings,它的实现更过彻底,完全使用byte[ ]来替代char[ ],同时新引入了一个字段coder来标识是LATIN1编码还是UTF16。

此文中的 value 为源码String类内部的char类型数组。

1、length()

用法:返回此字符串的长度;

public int length() {
    return value.length;
}
2、charAt()

用法: 返回指定索引处的 char 值;

String类中的charAt():

public char charAt(int index) {
	//value数组越过bound就会抛出异常
    if ((index < 0) || (index >= value.length)) {
        throw new StringIndexOutOfBoundsException(index);
    }
    return value[index];
}
3、compareTo()

用法:
1,长度相同, 从第一位开始比较,如果相同返回0,如果不同则马上返回这两个字符的ascii值的差值;
2,如果俩串的前k个字符一样,则返回长度差, 直接返回长度差值(k = Math.min(len1, len2))。

String中的compareTo:

public int compareTo(String anotherString) {
    int len1 = value.length;
    int len2 = anotherString.value.length;
    int lim = Math.min(len1, len2);
    char v1[] = value;
    char v2[] = anotherString.value;

    int k = 0;
    while (k < lim) {
        char c1 = v1[k];
        char c2 = v2[k];
        if (c1 != c2) {
        	返回ascii码差值
            return c1 - c2;
        }
        k++;
    }
    如果俩串的前k个字符一样,则返回长度差
    return len1 - len2;
}
4、compareToIgnoreCase()

用法:与compareTo用法一致,只是不考虑大小写

public int compareToIgnoreCase(String str) {
    return CASE_INSENSITIVE_ORDER.compare(this, str);
}

底层是compare方法,其中调用了toUpperCase() 方法和toLowerCase()方法,这俩方法作用是实现大小写字母之间的转换。
因此compareToIgnoreCase()可以忽略大小写比较。

public int compare(String s1, String s2) {
    int n1 = s1.length();
    int n2 = s2.length();
    int min = Math.min(n1, n2);
    for (int i = 0; i < min; i++) {
        char c1 = s1.charAt(i);
        char c2 = s2.charAt(i);
        if (c1 != c2) {
            c1 = Character.toUpperCase(c1);
            c2 = Character.toUpperCase(c2);
            if (c1 != c2) {
                c1 = Character.toLowerCase(c1);
                c2 = Character.toLowerCase(c2);
                if (c1 != c2) {
                    // No overflow because of numeric promotion
                    return c1 - c2;
                }
            }
        }
    }
    return n1 - n2;
}
5、concat()

用法:将指定字符串连接到此字符串的结尾。

public String concat(String str) {
        int otherLen = str.length();
		//如果传入的字符串为空,直接返回当前字符串
        if (otherLen == 0) {
            return this;
        }
        int len = value.length;
        //将value值复制到buf数组
        char buf[] = Arrays.copyOf(value, len + otherLen);
        //getChars() 方法将str复制到buf数组。底层调用System.arraycopy方法,是native方法。
        str.getChars(buf, len);
        return new String(buf, true);
    }
}
6、contains()

用法:当此字符串包含指定的 char 值序列时,返回 true。

调用了String indexOf()方法

public boolean contains(CharSequence s) {
	//int indexOf(String str): 返回指定字符在字符串中第一次出现处的索引,如果此字符串中没有这样的字符,则返回 -1。
    return indexOf(s.toString()) > -1;
}
7、startsWith()

用法:检测字符串是否以指定的前缀开始

//检测此字符串是否以指定的前缀开始。
public boolean startsWith(String prefix) {
    return startsWith(prefix, 0);
}
// 检测此字符串从指定索引开始的子字符串是否以指定前缀开始。
public boolean startsWith(String prefix, int toffset) {
    char ta[] = value;
    int to = toffset;
    char pa[] = prefix.value;
    int po = 0;
    int pc = prefix.value.length;
    // Note: toffset might be near -1>>>1.
    //如果toffset小于0, 或toffset大于value.length - pc时返回false;
    //这是因为当前String长达比传参字符串长度加上toffset还小的话,说明肯定不是以指定的前缀toffset开始。
    if ((toffset < 0) || (toffset > value.length - pc)) {
        return false;
    }
    //最多要比较pc个字符
    while (--pc >= 0) {
        if (ta[to++] != pa[po++]) {
            return false;
        }
    }
    return true;
}
8、equals

用法:重写了Object类中的方法,用于判断两个字符串是否相等(值比较)

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    //先判断anObject是不是String类的一个实例,不是则返回false
    if (anObject instanceof String) {
    	类型转换,把anObject赋给anotherString 
        String anotherString = (String)anObject;
        int n = value.length;
        //两个String对象长度相等才可能一样,长度不等返回false
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            遍历数组,比较 数组元素是否相同。如果有一个不同就返回false
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}
9、indexOf()

用法:返回在此字符串中第一次出现指定字符处的索引,从指定的索引开始搜索。找不到则返回 -1。

   static int indexOf(char[] source, int sourceOffset, int sourceCount,
                       char[] target, int targetOffset, int targetCount,
                       int fromIndex) {
        //1、当开始查找位置 大于等于 源字符串长度时,如果[查找字符串]为空,则:
        //返回字符串的长度,否则返回-1.
        if (fromIndex >= sourceCount) {
            return (targetCount == 0 ? sourceCount : -1);
        }
        //2、如果fromIndex 小于 0,则从 0开始查找。
        if (fromIndex < 0) {
            fromIndex = 0;
        }
        //3、如果[查找字符串]为空,则返回fromIndex
        if (targetCount == 0) {
            return fromIndex;
        }
        //4、开始查找,从[查找字符串]中得到第一个字符,标记为first
        char first  = target[targetOffset];
        //4.1、计算[源字符串最大长度]
        int max = sourceOffset + (sourceCount - targetCount);
        //4.2、遍历查找
        for (int i = sourceOffset + fromIndex; i <= max; i++) {
            //4.2.1、从[源字符串]中,查找到第一个匹配到[目标字符串]first的位置
            //for循环中,增加while循环
            /* Look for first character. */
            if (source[i] != first) {
                while (++i <= max && source[i] != first);
            }
            //4.2.2、如果在[源字符串]中,找到首个[目标字符串],
            //则匹配是否等于[目标字符串]
            /* Found first character, now look at the rest of v2 */
            if (i <= max) {
                //4.2.2.1、得到下一个要匹配的位置,标记为j
                int j = i + 1;
                //4.2.2.2、得到其余[目标字符串]的长度,标记为end
                int end = j + targetCount - 1;
                //4.2.2.3、遍历,其余[目标字符串],从k开始,
                //如果j不越界(小于end,表示:其余[目标字符串]的范围),
                //同时[源字符串]==[目标字符串],则
                //自增,继续查找匹配。j++、k++
                for (int k = targetOffset + 1; j < end && source[j] ==
                         target[k]; j++, k++);
                //4.2.2.4、如果j与end相等,则表示:
                //源字符串中匹配到目标字符串,匹配结束,返回i。
                if (j == end) {
                    /* Found whole string. */
                    return i - sourceOffset;
                }
            }
        }
        //其余情况,返回-1.
        return -1;
    }
10、split()

用法:根据匹配给定的正则表达式来拆分此字符串,limit :结果阈值

public String[] split(String regex, int limit) {
    /* fastpath if the regex is a
     (1)one-char String and this character is not one of the
        RegEx's meta characters ".$|()[{^?*+\\", or
     (2)two-char String and the first char is the backslash and
        the second is not the ascii digit or ascii letter.
     */
    char ch = 0;
    if (((regex.value.length == 1 &&
         ".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1) ||
         (regex.length() == 2 &&
          regex.charAt(0) == '\\' &&
          (((ch = regex.charAt(1))-'0')|('9'-ch)) < 0 &&
          ((ch-'a')|('z'-ch)) < 0 &&
          ((ch-'A')|('Z'-ch)) < 0)) &&
        (ch < Character.MIN_HIGH_SURROGATE ||
         ch > Character.MAX_LOW_SURROGATE))
    {
    /* off为要找的子字符串的起始位置,next为其结束位置,即分隔符所在的位置。list用来储存子字符串。
	而子字符串向list中添加的方式为list.add(substring(off, next));,也就是说,每次找到一个分隔符的时候,
	就会向list中添加一次,而substring函数中,若起始位置与终止位置为相邻的话,只会添加一个"",也就是我们
	在开始的实验中所看到的空的元素了。*/
        int off = 0;
        int next = 0;
        boolean limited = limit > 0;
        ArrayList<String> list = new ArrayList<>();
        while ((next = indexOf(ch, off)) != -1) {
            if (!limited || list.size() < limit - 1) {
                list.add(substring(off, next));
                off = next + 1;
            } else {    // last one
                //assert (list.size() == limit - 1);
                list.add(substring(off, value.length));
                off = value.length;
                break;
            }
        }
        // If no match was found, return this
        if (off == 0)
            return new String[]{this};

        // Add remaining segment
        if (!limited || list.size() < limit)
            list.add(substring(off, value.length));

        // Construct result
        int resultSize = list.size();
        if (limit == 0) {
            while (resultSize > 0 && list.get(resultSize - 1).length() == 0) {
                resultSize--;
            }
        }
        String[] result = new String[resultSize];
        return list.subList(0, resultSize).toArray(result);
    }
    return Pattern.compile(regex).split(this, limit);
}
11、 tocharArray()

用法: 将字符串转换为字符数组。
就是将String中存储char序列(字符序列)的char数组(char[])直接复制给一个新的char数组,
底层调用System.arraycopy方法,由于类初始化顺序问题,无法使用Array.copyOf()。可能是因为在jvm启动时要用到toCharArray()方法,但Arrays类还没有初始化。

public char[] toCharArray() {
    // Cannot use Arrays.copyOf because of class initialization order issues
    char result[] = new char[value.length];
    System.arraycopy(value, 0, result, 0, value.length);
    return result;
}
12、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;
}
13、valueOf()

用法: 该方法是静态方法,重载方法有很多。
传参是String、int、long、float、double类型的调用了toString()方法

public static String valueOf(int i) {
    return Integer.toString(i);
}

public static String valueOf(char c) {
    char data[] = {c};
    return new String(data, true);
}

参考资料

String.java源码解析之理解indexOf函数
从源码分析:Java中的split()方法

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值