String
JDK源码中的String类是Java中最常用的类之一,它提供了许多用于处理字符串的方法。以下是一些常用的String类方法:
-
构造方法:
String()
:创建一个空字符串。String(char[] value)
:根据字符数组创建一个新的字符串。String(byte[] bytes, int offset, int length)
:根据字节数组的一部分创建一个新的字符串。String(String original)
:根据另一个字符串创建一个新的字符串。
-
常用方法:
int length()
:返回字符串的长度。boolean isEmpty()
:判断字符串是否为空。char charAt(int index)
:返回指定索引处的字符。int indexOf(String str)
:返回指定子字符串在此字符串中第一次出现处的索引。int lastIndexOf(String str)
:返回指定子字符串在此字符串中最右边出现处的索引。String substring(int beginIndex)
:返回一个新字符串,它是此字符串的一个子字符串。String substring(int beginIndex, int endIndex)
:返回一个新字符串,它是此字符串的一个子字符串。boolean contains(CharSequence s)
:当且仅当此字符串包含指定的char值序列时,返回true。boolean equals(Object anObject)
:将此 String 与指定的对象比较。boolean equalsIgnoreCase(String anotherString)
:将此 String 与另一个 String 比较,不考虑大小写。int compareTo(String anotherString)
:按字典顺序比较两个字符串。String toLowerCase()
:将所有在此字符串中的字符都转换为小写。String toUpperCase()
:将所有在此字符串中的字符都转换为大写。String trim()
:返回字符串的副本,忽略前导空白和尾部空白。static String valueOf(Object obj)
:返回一个表示指定的对象的字符串。
-
不可变特性:
- String类是不可变的,这意味着一旦创建了String对象,就不能更改其内容。任何对String的操作都会生成一个新的String对象。
源码阅读
初始化
private final char value[];
hashCode()和equals()
hashCode源码:
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;
}
计算哈希值的方法如下:
- 初始化一个整数变量h为0。
- 如果h为0且字符串长度大于0,则遍历字符串中的每个字符。
- 对于每个字符,将其ASCII码值乘以31的幂(幂的次数等于当前字符的位置减去字符串长度),然后将结果累加到h上。
- 最后将计算出的哈希值赋给hash变量并返回。
这种哈希算法可以有效地减少哈希冲突的概率,提高字符串在哈希表中的查找效率。
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;
}
具体实现如下:
- 首先检查当前对象(this)是否与传入的对象(anObject)地址相同,如果相同则返回true。
- 如果传入的对象是一个String类型的实例,将其转换为String类型并赋值给anotherString变量。
- 获取当前字符串的长度n,并与anotherString的长度进行比较。如果长度不相等,则直接返回false。
- 如果长度相等,将当前字符串的字符数组value和anotherString的字符数组value分别赋值给v1和v2。
- 使用一个循环遍历两个字符数组,逐个比较对应位置的字符是否相等。如果有不相等的字符,则返回false。
- 如果所有字符都相等,则返回true。
- 如果传入的对象不是String类型或者长度不相等,则返回false。
StringBuffer
StringBuffer是Java中的一个类,它用于处理可变的字符串。与String不同,StringBuffer对象的内容可以在运行时被修改,而不需要创建新的实例。这使得StringBuffer在处理大量字符串操作时比String更高效。
以下是一些常用的StringBuffer方法:
- append():将指定的数据添加到当前StringBuffer对象的末尾。
- insert():在指定位置插入指定的数据。
- delete():删除指定范围内的字符。
- reverse():反转当前StringBuffer对象中的字符顺序。
- capacity():返回当前StringBuffer对象的容量。
- length():返回当前StringBuffer对象的长度。
- setCharAt():将指定位置的字符替换为指定的字符。
- charAt():返回指定位置的字符。
- toString():将StringBuffer对象转换为String对象。
示例代码:
public class StringBufferExample {
public static void main(String[] args) {
// 创建一个空的StringBuffer对象
StringBuffer sb = new StringBuffer();
// 使用append()方法添加字符串
sb.append("Hello");
sb.append(" ");
sb.append("World!");
// 输出结果
System.out.println(sb.toString()); // 输出:Hello World!
// 使用insert()方法在指定位置插入字符串
sb.insert(6, " Java");
System.out.println(sb.toString()); // 输出:Hello JavaWorld!
// 使用delete()方法删除指定范围的字符
sb.delete(6, 10);
System.out.println(sb.toString()); // 输出:HelloWorld!
// 使用reverse()方法反转字符串
sb.reverse();
System.out.println(sb.toString()); // 输出:!dlroWolleH
}
}
源码阅读
初始化方法
/**
* Constructs a string buffer initialized to the contents of the
* specified string. The initial capacity of the string buffer is
* {@code 16} plus the length of the string argument.
*
* @param str the initial contents of the buffer.
*/
public StringBuffer(String str) {
super(str.length() + 16);
append(str);
}
构造函数的参数是一个字符串str,它表示要初始化到字符串缓冲区的初始内容。
在构造函数内部,首先调用父类(CharSequence)的构造函数,传入一个长度值。这个长度值是字符串str的长度加上16。这样做是为了确保字符串缓冲区有足够的容量来容纳初始内容以及可能的后续修改。
然后,使用append(str)方法将字符串str的内容添加到字符串缓冲区中。这样,字符串缓冲区就包含了传入的字符串作为其初始内容。
append
public synchronized StringBuffer append(Object obj) {
toStringCache = null;
super.append(String.valueOf(obj));
return this;
}
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));
}
}
第一个方法是append(Object obj)
,它是一个同步方法,用于将一个对象添加到字符串缓冲区的末尾。首先,它将toStringCache
设置为null,然后调用父类的append
方法,将对象转换为字符串并添加到缓冲区。最后返回当前对象(即字符串缓冲区本身)。
第二个方法是append(String str)
,它是AbstractStringBuilder类的一个抽象方法,用于将一个字符串添加到字符串缓冲区的末尾。如果传入的字符串为null,它会调用appendNull()
方法。否则,它会计算新字符串的长度,并确保缓冲区有足够的容量来容纳新的字符串。然后,使用getChars()
方法将字符串的字符复制到缓冲区的字符数组中,并更新缓冲区的计数器。最后返回当前对象(即字符串缓冲区本身)。
第三个方法是ensureCapacityInternal(int minimumCapacity)
,它是一个私有方法,用于确保字符串缓冲区有足够的容量来容纳至少minimumCapacity
个字符。如果需要的容量大于当前缓冲区的容量,它会创建一个新的字符数组,并将旧的字符数组的内容复制到新数组中。
StringBuilder
StringBuilder是一个可变的字符序列,主要用于高效地拼接字符串。
StringBuilder类提供了与StringBuffer兼容的API,但不包括同步,这使得它在单线程中通常比StringBuffer性能更高。因此,在不需要考虑线程安全的情况下,建议使用StringBuilder代替StringBuffer。
源码阅读
初始化方法
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}
构造函数的参数是一个字符串str
,它表示要初始化到字符串构建器的初始内容。
在构造函数内部,首先调用父类(CharSequence)的构造函数,传入一个长度值。这个长度值是字符串str
的长度加上16。这样做是为了确保字符串构建器有足够的容量来容纳初始内容以及可能的后续修改。
然后,使用append(str)
方法将字符串str
的内容添加到字符串构建器中。这样,字符串构建器就包含了传入的字符串作为其初始内容。
append
public StringBuilder append(String str) {
super.append(str);
return this;
}
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
第一个方法是append(String str)
,它是StringBuilder类的一个公共方法,用于将一个字符串添加到字符串构建器的末尾。它首先调用父类的append
方法,将字符串添加到字符数组中,然后返回当前对象(即字符串构建器本身)。
第二个方法是append(String str)
,它是AbstractStringBuilder类的一个抽象方法,用于将一个字符串添加到字符串构建器的末尾。如果传入的字符串为null,它会调用appendNull()
方法。否则,它会计算新字符串的长度,并确保缓冲区有足够的容量来容纳新的字符串。然后,使用getChars()
方法将字符串的字符复制到缓冲区的字符数组中,并更新缓冲区的计数器。最后返回当前对象(即字符串构建器本身)。
总结
String | StringBuilder | StringBuffer | |
---|---|---|---|
类别 | 不可变字符串 | 可变字符串 | 可变字符串与同步 |
性能 | 因为不可变,拼接字符串效率较低,每次拼接都会生成新的String对象。 | 在单线程中,相比String效率更高,因为可以直接在原字符串上进行修改,不需要创建新对象。 | 由于添加了同步,所以在多线程环境中是安全的,但在单线程中比StringBuilder慢。 |
线程安全 | 线程安全,因为是不可变的 | 不是线程安全的 | 线程安全 |
修改方式 | 不能被修改 | 可以被修改 | 可以被修改 |
常见用途 | 适用于少量字符串操作和值的比较 | 适用于大量字符串操作且不需要考虑线程安全的场景 | 适用于大量字符串操作且需要考虑线程安全的场景 |
内部实现 | 使用字符数组实现 | 使用可变字符数组实现 | 使用和StringBuilder相同的可变字符数组实现,但添加了同步 |
API提供 | 自Java 1.0起存在 | 自Java 1.5起引入 | 自Java 1.2起引入 |
相同之处:
- 都是用来处理字符串的。
- 都可以包含相同的字符序列。
区别:
- 性能:
String
在拼接时会创建多个对象,而StringBuilder
和StringBuffer
则在原有对象上进行修改,因此在性能上StringBuilder
和StringBuffer
优于String
。 - 线程安全:
String
是不可变的,自然是线程安全的;StringBuffer
是线程安全的,因为它的关键方法都被synchronized
修饰;StringBuilder
则不是线程安全的。 - API版本:
String
从Java 1.0开始就有,而StringBuilder
和StringBuffer
分别从Java 1.5和1.2引入。 - 修改能力:
String
是不可修改的,而StringBuilder
和StringBuffer
可以进行各种修改操作(如追加、插入、删除等)。 - 适用场景: 在需要多线程共享字符串时应该使用
StringBuffer
,在单线程或不需要线程安全的情况下,应优先选择性能更高的StringBuilder
。