java数据结构-String
开篇说明
最近在准备跳槽,目标岗位是后台java工程师,但是对java中的数据结构总是今天记住明天就忘了😭,因此开此专栏,边记录边刷题,每天进步一点点,争取早日上岸!
String
String在java里是最简单的数据结构了,它是一个final类,至于final的具体原理,先埋个坑,之后把final文章写好就粘过来。反正知道final修饰的话,String类是不能被继承的,并且成员方法method都会隐式被指定为final方法,成员属性可以根据自己的需要设置。
图书馆里正在码字的我看到身边一群年轻人刷刷地走进了会议室,突然好羡慕人家丰富多彩的周末呀,看来以后要多参加活动了=.=
- String类是通过char数组来保存字符串的
- string类真的有好多构造方法,但都是数组相关的,可以直接拿来用
- 求string的长度是string.length(),这是注意是有括号的,要和数组长度求解无括号区分开来
- String对象一旦被创建就是固定不变的,对String对象的任何改变都会生成新的对象
String使用private final char value[] 来实现字符串的存储,所以String对象创建后,就不能再修改此对象存储的字符串内容,所以说String类型是不可变的。
所有可以修改字符串的功能都是通过创建一个新的字符串对象来实现的,而不是对原来的字符串对象进行修改。
字符串常量池
-
JVM为了提高性能和减少内存开销,在实例化字符串时进行了优化:
每当创建字符串常量时,JVM会首先检查字符串常量池,如果该字符串已经存在常量池中,那么就直接返回常量池中的实例引用。如果字符串不在常量池中,就会实例化该字符串,并将其放在常量池中 -
由于字符串的不可变性可以肯定,常量池中一定不存在两个相同的字符串
-
常量池分为:静态常量池和运行时常量池。
静态常量池:*.class文件中的常量池,class文件中的常量池不仅仅包含字符串、数字等字面量,还包含类、方法等信息
运行时常量池:JVM虚拟机完成类装载操作后,将class文件中的常量池载入到内存中,并保存在方法区中
常说的常量池,就是指方法区中的运行时常量池
此处留个坑,对于方法区常量池,学习到JVM后补。
接下来进行一波薛定谔的字符串比较:
String a = "hello";
String b = "world";
String origin = "helloworld";
String res = a + b;
String res2 = "hello" + "world";
System.out.println(origin==res); // false
System.out.println(origin==res2); // true
System.out.println(res==res2); //false
当一个字符串由多个字符串常量连接而成时,它自己肯定也是字符串常量,所以origin和res2比较结果为true;
**JVM对String a = "hello"
对象放在常量池中是在编译时做的,而String res = a + b;
是在运行时刻才能知道的,new对象也是在运行时才做的。所以origin和res结果为false;
分析步骤:
1)栈中开辟一块空间存放引用a,a指向String常量"hello"。
2)栈中开辟一块空间存放引用b,b指向String常量"world"。
3)栈中开辟一块空间存放引用origin,origin指向字符串常量"helloworld"。
4)栈中开辟一块空间存放引用res。
5)a+b通过StringBuilder的最后一步toString()方法还原一个新的String对象"helloworld",因此堆中开辟一块空间存放此对象
6)引用res指向堆中的新String对象。
7)res指向的对象在堆中,常量"helloworld"在常量池中,所以输出为false。
编译器和运行期等概念留坑
总结:字面量“+”拼接是在编译器进行,拼接后的字符串存放在字符串常量池中;而字符串引用的“+”拼接运算是在运行时进行的,新创建的字符串放在堆中。由此可见,使用字面量+拼接效率更高
- new String创建的对象会存储在heap中,是在运行期创建的
以下存疑,仅作参考
new创建字符串时首先会查看常量池中是否有相同值的字符串,如果有,拷贝一份到堆中,然后返回堆中的地址;如果常量池中没有,则在堆中创建一份,然后返回堆中的地址。
String类中方法说明
- String.intern()
说明:调用intern方法时,如果池中已经包含一个等于此String对象的字符串(是否等于由equals方法判定),则返回池中的字符串。否则,将此String对象添加到池中,并返回此String对象的引用。
String s0 = "hello";
char[] chars = {'h','e','l', 'l','o'};
String s1 = new String(chars);
System.out.println(s0==s1); // false
System.out.println(s0==s1.intern()); // true
s0指向常量池中的对象,s1指向堆中的对象,但是s1.intern方法调用后将s1指向的堆中的对象放入了常量池中,所以第一个结果是false,第二个结果是true。
- String.isEmpty()
说明:此方法实现原理是判断字符数组的长度是否等于0,但是平时总会忘记有这么个方法,特别记忆下。
- String.charAt(int)
说明:返回指定下标处的字符值
- String.codePointAt(int)
说明:返回指定下标处的unicode代码值
String s0 = "大俊加油";
System.out.println(s0.charAt(0)); // 大
System.out.println(s0.codePointAt(0)); // 22823
- String.getChars(int begin, int end, char[] chars, int dstBegin)
说明:将string的值复制进数组,改变数组的值(不常用)
String s0 = "hello";
char[] chars = {'w', 'o', 'r', 'l', 'd'};
s0.getChars(0, 3, chars, 2);
System.out.println(s0); // hello
System.out.println(Arrays.toString(chars)); // [w, o, h, e, l]
- 字符串比较方法
String.equals(object) | String.compareTo(string) | String.contentEquals(charSequence) |
---|---|---|
object是一个完全相同的字符串时返回true | 两个string对象的每个字符进行比较,相同返回0,不同返回比较字符的差值 | charSequence对象是stringbuilder/stringbuffer,且字符序列相同返回true |
忽略大小写:String.equalsIgnoreCase(string) | String.compareToIgnoreCase(string) |
说明:字符串比较方法众多,和不同对象比较也有对应的方法,记不清时可以都把他们转成string,使用compareTo和compareToIgnoreCase方法
- equals和==
- equals定义在类object中,用来判断两个对象是否相等(判断是否是同一个对象)。
- 类没有覆盖equals时,通过equals比较对象,实际上是通过==判断两个对象的地址是否相等。
- String覆盖了equals,所以使用string调用equals,是判断两个string对象的内容是否相等。
- String.startsWith(s0, int)
说明:从int为下标的字符开始,String以s0开始则返回true
- String.endsWith(s1)
说明:string以s1结束则返回true
- String.hashCode()
说明: 根据一定的算法返回一串哈希码
- 哈希码作用是确定该对象在哈希表中的索引位置(我也不太懂)
- hashCode()定义在object类中,意味着java中任何类都具有hashCode函数
- hashCode()在散列表中才有用,作用是获取对象的散列码,进而确定该对象在散列表中的位置。
- 散列表存储的是键值对,特点是根据键快速检索对应的值。其中就利用了散列码。散列表本质是数组,数组的位置,是通过键对应的散列码得到的。
- 若两个元素相等,它们的散列码一定相等;反过来不一定。
- 在散列表的数据结构中判断两个对象是否相等,除了覆盖equals方法,还要覆盖hashCode()方法。
- String.indexOf(string)
String.indexOf(int) | String.indexOf(string) | String.lastIndexOf(int) | String.lastIndexOf(string) |
---|---|---|---|
根据unicode字符返回对应下标 | 根据字符返回对应下标,调用charAt()方法 | 根据unicode字符返回最后一次匹配下标 | 根据字符返回最后一次匹配下标 |
String s0 = "ohello";
System.out.println(s0.indexOf("o")); // 1
System.out.println(s0.lastIndexOf("o")); // 5
- String.substring(int)/ String.substring(int, int) / String.subSequence(int, int)
说明:返回子字符串
- String.concat(string)
说明:连接字符串
注意和+实现方式区别:
- “+”连接字符串是通过创建StringBuffer/StringBuilder对象,调用append方法,得到一个拼接的StringBuffer或StringBuilder对象,最后调用toString方法,返回得到的String对象的地址
- concat如果string为null,返回当前字符串。否则根据字符数组的方法实现。
- String.replace(char oldChar, char newChar)
说明:newChar替换所有oldChar,返回一个新的字符串
-
String.matches(String regex)
note: 判断是否匹配regex -
String.contains(charSequence)
note:判断是否包含charSequence -
String.split(string)
note:用给定的string拆分字符串,返回字符串数组 -
String.join(CharSequence delimiter, CharSequence… elements)
note:使用delimiter拼接字符串
String s0 = String.join("-", "hello", "da", "jun");
System.out.println(s0); // hello-da-jun
- String.toLowerCase()
note:返回小写string - String.toUpperCase()
note:返回大写String - String.trim()
note: 删除了前后空格 - String.toCharArray()
note:将string转换成字符数组返回 - String.valueOf(object)/String.copyValueOf()
note:将其他对象转换成string对象