public class StringHashCode {
public static void main(String[] args) {
\\输出结果相同
String[] hellos = "Hello Hello".split(" " );
System.out.println(""+hellos[0].hashCode());
System.out.println(""+hellos[1].hashCode());
\\输出结果相同
String a = new String("hello");
String b = new String("hello");
System.out.println(""+a.hashCode());
System.out.println(""+b.hashCode());
}
}
结论
String 类是final类,不可以继承。对String类型最好的重用方式是组合 而不是继承。
String 有length()方法,数组有length属性
String s = new String(“xyz”); 创建了几个字符串对象?
两个对象,一个静态存储区“xyz”, 一个用new创建在堆上的对象。
String 和 StringBuffer,String Builder区别?
在大部分情况下 StringBuffer > String
Java.lang.StringBuffer 是线程安全的可变字符序列。一个类似于 String 的字符串缓冲区,但不能修改。虽然在任意时间点上它都包含某种特定的字符序列,但通过某些方法调用可以改变该序列的长度和内容。在程序中可将字符串缓冲区安全地用于多线程。而且在必要时可以对这些方法进行同步,因此任意特定实例上的所有操作就好像是以串行顺序发生的,该顺序与所涉及的每个线程进行的方法调用顺序一致。
StringBuffer 上的主要操作是 append 和 insert 方法,可重载这些方法,以接受任意类型的数据。每个方法都能有效地将给定的数据转换成字符串,然后将该字符串的字符追加或插入到字符串缓冲区中。append 方法始终将这些字符添加到缓冲区的末端;而 insert 方法则在指定的点添加字符。
例如,如果 z 引用一个当前内容是 “start”的字符串缓冲区对象,则此方法调用 z.append(“le”) 会使字符串缓冲区包含 “startle”( 累加); 而 z.insert(4, “le”) 将更改字符串缓冲区,使之包含 “starlet”。
在大部分情况下 StringBuilder > StringBuffer
java.lang.StringBuilder 一个可变的字符序列是 JAVA 5.0 新增的。此类提供一个与 StringBuffer 兼容的 API,但不保证同步,所以使用场景是单线程。该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。如果可能,建议优先采用该类,因为在大多数实现中,它比 StringBuffer 要快。两者的使用方法基本相同。
源码
String,StringBuffer,StringBuilder都实现了CharSequence接口。
public class StringHashCode {
public static void main(String[] args) {
\\输出结果相同
String[] hellos = "Hello Hello".split(" " );
System.out.println(""+hellos[0].hashCode());
System.out.println(""+hellos[1].hashCode());
\\输出结果相同
String a = new String("hello");
String b = new String("hello");
System.out.println(""+a.hashCode());
System.out.println(""+b.hashCode());
}
}
String的源码
public final class String{
private final char value[]; // used for character storage
private int the hash; // cache the hash code for the string
}
成员变量只有两个:
final的char类型数组
int类型的hashcode
构造函数
public String()
public String(String original){
this.value = original.value;
this.hash = original.hash;
}
public String(char value[]){
this.value = Arrays.copyOf(value, value.length);
}
public String(char value[], int offset, int count){
// 判断offset,count,offset+count是否越界之后
this.value = Arrays.copyOfRange(value, offset, offset+count);
}
这里用到了一些工具函数
copyOf(source[],length); 从源数组的0位置拷贝length个;
这个函数是用System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength))实现的。
copyOfRange(T[] original, int from, int to)。
构造函数还可以用StringBuffer/StringBuilder类型初始化String,
public String(StringBuffer buffer) {
synchronized(buffer) {
this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
}
}
public String(StringBuilder builder) {
this.value = Arrays.copyOf(builder.getValue(), builder.length());
}
除了构造方法,String类的方法有很多,
length,isEmpty,可以通过操作value.length来实现。
charAt(int index):
通过操作value数组得到。注意先判断index的边界条件
public char charAt(int index) {
if ((index < 0) || (index >= value.length)) {
throw new StringIndexOutOfBoundsException(index);
}
return value[index];
}
getChars方法
public void getChars(int srcBegin, int srcEnd,
char dst[], int dstBegin)
{
\\边界检测
System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}
equals方法,根据语义相等(内容相等,而非指向同一块内存),重新定义了equals
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof 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) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
如果比较的双方指向同一块内存,自然相等;(比较==即可)
如果内容相等,也相等,比较方法如下:
首先anObject得是String类型(用关键字instanceof)
然后再比较长度是否相等;
如果长度相等,则挨个元素进行比较,如果每个都相等,则返回true.
还有现成安全的与StringBuffer内容比较
contentEquals(StringBuffer sb),实现是在sb上使用同步。
compareTo():
如果A大于B,则返回大于0的数;
A小于B,则返回小于0的数;
A=B,则返回0
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) {
return c1 - c2;
}
k++;
}
return len1 - len2;
}
regionMatches:如果两个字符串的区域都是平等的,
public boolean regionMatches(int toffset, String other, int ooffset, int len) { //判断边界条件 while (len-- > 0) { if (ta[to++] != pa[po++]) { return false; } } } public boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len) { while (len-- > 0) { char c1 = ta[to++]; char c2 = pa[po++]; if (c1 == c2) { continue; } if (ignoreCase) { // If characters don't match but case may be ignored, // try converting both characters to uppercase. // If the results match, then the comparison scan should // continue. char u1 = Character.toUpperCase(c1); char u2 = Character.toUpperCase(c2); if (u1 == u2) { continue; } // Unfortunately, conversion to uppercase does not work properly // for the Georgian alphabet, which has strange rules about case // conversion. So we need to make one last check before // exiting. if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) { continue; } } return false; } return true; } startsWith(String prefix, int toffset) startsWith(String prefix) endsWith(String suffix) { return startsWith(suffix, value.length - suffix.value.length); } substring(int beginIndex,int endIndex) |
除了条件判断:
return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
字符串连接concat(String str)
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new String(buf, true);
对于StringBuffer和StringBuilder
StringBuffer 和 StringBuilder 都是继承于 AbstractStringBuilder, 底层的逻辑(比如append)都包含在这个类中。
public AbstractStringBuilder append(String str) {
if (str == null) str = "null";
int len = str.length();
ensureCapacityInternal(count + len);//查看使用空间满足,不满足扩展空间
str.getChars(0, len, value, count);//getChars就是利用native的array copy,性能高效
count += len;
return this;
}
StringBuffer 底层也是 char[], 数组初始化的时候就定下了大小, 如果不断的 append 肯定有超过数组大小的时候,我们是不是定义一个超大容量的数组,太浪费空间了。就像 ArrayList 的实现,采用动态扩展,每次 append 首先检查容量,容量不够就先扩展,然后复制原数组的内容到扩展以后的数组中。