差点忘了最经常使用的String类型,咱们对String的大多数方法都已经很熟了,这里就挑几个平时不会直接接触的点来解析一下。java
先来看看它的成员变量数组
public final class String { private final char value[]; private int hash; // Default to 0 }
public int hashCode() { int h = hash; if (h == 0 && value.length > 0) { char val[] = value; for (int i = 0; i < value.length; i++) { h = 31 * h + val[i]; } hash = h; } return h; }
String的哈希值采用一种延迟计算的策略,计算方法很简单,就是把每一个char都当作int,经过公式h=31*h+char的计算最终值。因为String是不可变对象,在生命周期内,hash值只须要计算一次。缓存
public String substring(int beginIndex, int endIndex) { return ((beginIndex == 0) && (endIndex == value.length)) ? this : new String(value, beginIndex, subLen); } public String(char value[], int offset, int count) { this.value = Arrays.copyOfRange(value, offset, offset+count); }
注:上面的代码为了节省空间,省略了异常检查逻辑安全
subString会建立一个全新的字符串,而不是共享原字符串的char数组。按道理讲,因为String是不可变的,那么subString和原string共享char数组是安全的,多是出于其余方面的考虑:虽然节省了一点空间,可是须要额外增长一个offset和size成员,总体效率未必更佳。性能
简单介绍一下码点和码元的概念,一个码点是某种字符编码方案里面,某个字符对应的绝对编码值。而码元则是指具体的字符存储方案下,最小的存储单元。this
对java的String来讲,存储的是unicode字符,使用的编码方案是utf-16,码元是char(16bit); utf-16的大部分码点用一个char足够了,可是有少部分须要两个char。编码
咱们来看一段从码点序列建立String的代码:code
public String(int[] codePoints, int offset, int count) { final int end = offset + count; // Pass 1: Compute precise size of char[] int n = count; for (int i = offset; i < end; i++) { int c = codePoints[i]; if (Character.isBmpCodePoint(c)) continue; else if (Character.isValidCodePoint(c)) n++; else throw new IllegalArgumentException(Integer.toString(c)); } // Pass 2: Allocate and fill in char[] final char[] v = new char[n]; for (int i = offset, j = 0; i < end; i++, j++) { int c = codePoints[i]; if (Character.isBmpCodePoint(c)) v[j] = (char)c; else Character.toSurrogates(c, v, j++); } this.value = v; }
基本多文种平面
码点,这样的码点只需1个char所以,若是你要把String当作一段实际的文本,并处理当中的单个文字,经过遍历char的方式是不行的,而要使用codePoint相关方法。关于编码相关的知识,不是本文要讲的内容,你们自行查阅资料。对象