java重拾-数组&字符串
java重拾-数组&字符串
数组
基于 C/C++ 语言实现
int[] anArray;
int anOtherArray[];
int[] anArray = new int[10];
int anOtherArray[] = new int[] {1, 2, 3, 4, 5};
for (int element : anOtherArray) {
System.out.println(element);
}
数组属于一种基本的操作类型,本质是一个指针,指向一段具有相同排列的连续内存的起始位置,并没有封装任何高级方法
若是希望使用类似std的方法,将数组转为List,List是一个接口,任一实现类都实现了许多有用且高效的方法
List<Integer> aList = Arrays.stream(anArray).boxed().collect(Collectors.toList());
看不懂的流
Arrays.sort
int[] anArray = new int[] {5, 2, 1, 4, 8};
Arrays.sort(anArray);
Arrays.binarySearch
int[] anArray = new int[] {1, 2, 3, 4, 5};
int index = Arrays.binarySearch(anArray, 4);
Arrays.copyOfRange
底层调用的是 System.arraycopy()
方法,这个方法是一个 native 方法,它是用 C/C++ 实现的,效率非常高
int[] array1 = {1, 2, 3};
int[] array2 = {4, 5, 6};
// 创建一个新数组,长度为两个数组长度之和
int[] mergedArray = new int[array1.length + array2.length];
// 复制第一个数组到新数组
System.arraycopy(array1, 0, mergedArray, 0, array1.length);
System.out.println(Arrays.toString(mergedArray));
// 复制第二个数组到新数组
System.arraycopy(array2, 0, mergedArray, array1.length, array2.length);
System.out.println(Arrays.toString(mergedArray));
ArrayIndexOutOfBoundsException
数组越界
二维数组
即指针的数组
A->B[]
B[i]->C[]
打印数组
Arrays.asList(cmowers).stream().forEach(s -> System.out.println(s));
Stream.of(cmowers).forEach(System.out::println);
Arrays.stream(cmowers).forEach(System.out::println);
Arrays.toString()
Arrays.deepToString()
字符串
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
}
- final 创建即不变, 在生命周期中只会指向同一个内存地址
- serializable 可序列化
- comparable 建议使用compareTo, equals进行==比较
- CharSequence可以拼接, 但是因为string是final的, 每次拼接都会产生一个新的, 内存开销大, 建议使用 StringBuffer 和 StringBuilder
底层实现
private final char value[];
private final byte value[];
使用
private final byte coder;
来区分编码方式, 若满足 ASCII则使用byte存储, 如果有中文登UTF8才支持的字符则使用 char
HashCode
每一个字符串都会有一个 hash 值,这个哈希值在很大概率是不会重复的
private int hash; // 缓存字符串的哈希码
public int hashCode() {
int h = hash; // 从缓存中获取哈希码
// 如果哈希码未被计算过(即为 0)且字符串不为空,则计算哈希码
if (h == 0 && value.length > 0) {
char val[] = value; // 获取字符串的字符数组
// 遍历字符串的每个字符来计算哈希码
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i]; // 使用 31 作为乘法因子
}
hash = h; // 缓存计算后的哈希码
}
return h; // 返回哈希码
}
hashCode 方法首先检查是否已经计算过哈希码,如果已经计算过,则直接返回缓存的哈希码。否则,方法将使用一个循环遍历字符串的所有字符,并使用一个乘法和加法的组合计算哈希码。
这种计算方法被称为“31 倍哈希法”
H(s) = (s[0] * 31^(n-1)) + (s[1] * 31^(n-2)) + ... + (s[n-1] * 31^0)
用于给长string 标一个id, 复用时直接返回hashcode相同的指针即可
字符串常量池
String s = new String("二哥");
创建了两个对象
- 常量池的对象
- 堆中的对象
返回堆中的引用
因为双引号不存在常量池的字符串, 都会在常量池注册一次, 同时使用new关键字一定会产生一个新对象
因此产生了两个对象
String s = "三妹";
则不会硬性地产生新对象, 而是查找常量池后直接返回常量池的引用
又因为string是final的, 这样做并不会对常量池的对象产生影响
若是通过字符串操作产生的字符串就不会在常量池中注册,, 正所谓"常量"嘛
但是若我们希望动态地加入一些"常量"可以使用String.intern()方法
此时会在常量池中保存堆的对象的引用, 并不会产生一个新的对象, 因为java7之后字符串常量池放在了堆中
String s1 = new String("二哥三妹");
String s2 = s1.intern();
System.out.println(s1 == s2);//false
String s1 = new String("二哥") + new String("三妹");
String s2 = s1.intern();
System.out.println(s1 == s2);//true
不过需要注意的是,尽管 intern 可以确保所有具有相同内容的字符串共享相同的内存空间,但也不要烂用 intern,因为任何的缓存池都是有大小限制的,不能无缘无故就占用了相对稀缺的缓存空间,导致其他字符串没有坑位可占。
StringBuffer&StringBuilder
Buffer有同步锁, Builder无同步锁, 在同步场景可以使用ThreadLocal避免一部分冲突
Builder效率高, 主要使用他
java编译器会自动将字符串拼接优化为Builder的拼接
StringBuilder的实现
使用原生char[], count, capacity实现操作, 不依赖String
append会检查count与capacity的大小, 若是不够大会调用ensureCapacityInternal扩容
扩容策略是n=2n+2
判断相等
- == 用于判断基本数据类型的值相等
.equals()
方法用于比较两个对象的内容是否相等
String alita = new String("小萝莉");
String luolita = new String("小萝莉");
System.out.println(alita.equals(luolita)); // true
System.out.println(alita == luolita); // false
- Objects.equals()
Objects.equals()
这个静态方法的优势在于不需要在调用之前判空。
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}
- String 类的
.contentEquals()
.contentEquals()
的优势在于可以将字符串与任何的字符序列(StringBuffer、StringBuilder、String、CharSequence)进行比较。
public boolean contentEquals(CharSequence cs) {
// Argument is a StringBuffer, StringBuilder
if (cs instanceof AbstractStringBuilder) {
if (cs instanceof StringBuffer) {
synchronized(cs) {
return nonSyncContentEquals((AbstractStringBuilder)cs);
}
} else {
return nonSyncContentEquals((AbstractStringBuilder)cs);
}
}
// Argument is a String
if (cs instanceof String) {
return equals(cs);
}
// Argument is a generic CharSequence
int n = cs.length();
if (n != length()) {
return false;
}
byte[] val = this.value;
if (isLatin1()) {
for (int i = 0; i < n; i++) {
if ((val[i] & 0xff) != cs.charAt(i)) {
return false;
}
}
} else {
if (!StringUTF16.contentEquals(val, cs, n)) {
return false;
}
}
return true;
}
拼接
编译器自动优化为双引号或builder append
- concat
原理与builder类似, 但每次concat都会产生新string对象, 开销大
- String.join
使用StringJoiner实现, 其本质仍是builder