java 字符串
String、StringBuffer和StringBuilder的区别
参考链接:https://www.cnblogs.com/weibanggang/p/9455926.html
https://www.cnblogs.com/onetheway2018/p/11553168.html
string stringbuilder 相互转化
String s = "123"; StringBuilder sn = new StringBuilder(s); System.out.println(sn.toString());
string stringbuilder 的拷贝
StringBuilder a = new StringBuilder("defwrv"); StringBuilder b = a; b.append("111"); System.out.println(b);//defwrv111 System.out.println(a);//defwrv111 // String a = "1234"; // String b = a; // b += "000"; // System.out.println(b);//1234000 // System.out.println(a);//1234
String 和int | double 的转化
// String s = "123"; // int x = Integer.parseInt(s); // System.out.println(x);//123 // String sp = String.valueOf(x); // System.out.println(sp);//123 String s = "123.06"; double x = Double.parseDouble(s); System.out.println(x); String sp = String.valueOf(x); System.out.println(sp);
运行速度
StringBuilder > StringBuffer > String
String
真正不可变有下面几点原因:
- 保存字符串的数组(字符数组)被
final
修饰且为私有的,并且String
类没有提供/暴露修改这个字符串的方法。 String
类被final
修饰导致其不能被继承,进而避免了子类破坏String
不可变。
String最慢的原因:String为字符串常量,而StringBuilder和StringBuffer均为字符串变量,即String对象一旦创建之后该对象是不可更改的,但后两者的对象是变量,是可以更改的
public static void main(String[] args) { long a = new Date().getTime(); String cc = ""; int n = 10000; for (int i = 0; i < n; i++) { cc += "." + i; } System.out.println("String使用的时间" + (System.currentTimeMillis() - a) / 1000.0 + "s"); long s1 = System.currentTimeMillis(); StringBuilder sb = new StringBuilder(); for (int i = 0; i < n; i++) { sb.append("." + i); } System.out.println("StringBuilder使用的时间" + (System.currentTimeMillis() - s1) / 1000.0 + "s"); long s2 = System.currentTimeMillis(); StringBuffer sbf = new StringBuffer(); for (int i = 0; i < n; i++) { sbf.append("." + i); } System.out.println("StringBuffer使用的时间" + (System.currentTimeMillis() - s2) / 1000.0 + "s"); } // String使用的时间0.161s // StringBuilder使用的时间0.003s // StringBuffer使用的时间0.004s
线程安全
在线程安全上,StringBuilder是线程不安全的,而String和StringBuffer是线程安全的
如果一个StringBuffer对象在字符串缓冲区被多个线程使用时,StringBuffer中很多方法可以带有synchronized关键字,所以可以保证线程是安全的,但StringBuilder的方法则没有该关键字,所以不能保证线程安全,有可能会出现一些错误的操作。所以如果要进行的操作是多线程的,那么就要使用StringBuffer,但是在单线程的情况下,还是建议使用速度比较快的StringBuilder。
(一个线程访问一个对象中的synchronized(this)同步代码块时,其他试图访问该对象的线程将被阻塞)
性能
每次对 String
类型进行改变的时候,都会生成一个新的 String
对象,然后将指针指向新的 String
对象。StringBuffer
每次都会对 StringBuffer
对象本身进行操作,而不是生成新的对象并改变对象引用。
相同情况下使用 StringBuilder
相比使用 StringBuffer
仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险
总结
String:适用于少量的字符串操作的情况
StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况
StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况
为什么要把String设计为不变量?
- 节省空间:字符串常量存储在JVM的字符串池中可以被用户共享。
- 提高效率:String会被不同线程共享,是线程安全的。在涉及多线程操作中不需要同步操作。
- 安全:String常被用于用户名、密码、文件名等使用,由于其不可变,可避免黑客行为对其恶意修改。
String用法
import java.text.*; import java.math.*; import java.util.*; public class Main { public static void main(String[] args) { String s = "ababacdba"; System.out.println(s.charAt(1));//b char sp[] = s.toCharArray(); // 比较 String a = "aba"; String b = "ABa"; System.out.println(a.equals(b));//false System.out.println(a.equalsIgnoreCase(b));//true System.out.println("ab".compareTo("al"));//-10 // 查找 String m = "ababdfaba"; String n = "aba"; System.out.println(m.contains(n));//true 找子字符串 System.out.println(m.indexOf(n));//0 System.out.println(m.indexOf("abc"));//-1找不到 System.out.println(m.indexOf("abc", 4));//-1找不到 System.out.println(m.startsWith("ab"));//true System.out.println(m.startsWith("ab", 6));//true System.out.println(m.endsWith("ba"));//true // 替换 不要用replaceAll String str = "helloworld"; System.out.println(str.replace("l", "_"));//he__owor_d System.out.println(str.replaceFirst("l", "_"));//he_loworld // 截取 String st = "helloworld"; System.out.println(st.substring(5));//world System.out.println(st.substring(0, 5));// [0,5) hello } }
heloworld
heoworld
StringBuilder用法
public static void main(String[] args) { StringBuilder string = new StringBuilder(); string.append("abcd"); System.out.println(string.toString());// 转换为String对象 string.insert(3, "123"); System.out.println(string.toString()); string.delete(3, 5);// [3,5)//deleteCharAt(index) System.out.println(string.toString()); System.out.println(string.length()); string.replace(1, 3, "o"); System.out.println(string.toString());// [1,3) string.replace(3, 4, "m"); System.out.println(string.toString()); // abcd // abc123d // abc3d // 5 // ao3d // ao3m }
StringBuilder类型字符串中的字符换位置
StringBuilder sb = new StringBuilder(s);
char x = sb.charAt(0);
char y = sb.charAt(3);
sb.setCharAt(0, y);
sb.setCharAt(3, x);
//做到了把位置在0和3的字符互换了位置。
一些处理字符串的方法
trim()的作用是去掉字符串两端的多余的空格,注意,是两端的空格,且无论两端的空格有多少个都会去掉,当然
中间的那些空格不会被去掉
String s = " dsf fg "; s = s.trim(); System.out.println(s); dsf fg
.split()
String s = "2019-06-30"; String[] a = s.split("-");//双引号,String 类型 for (String x : a) { System.out.println(x); }
2019
06
30
String和字符数组转换
public String sortt(String s) { char[] ar = s.toCharArray(); Arrays.sort(ar); return new String(ar); }
实现两个字符串获取两个指定字符串中的最大相同子串
import java.util.*; public class Main { public String same(String s1, String s2) { String min, max; if (s1.length() > s2.length()) { min = s2; max = s1; } else { min = s1; max = s2; } //一次外循环 表示一种长度的子串:min.length()-i为子串长度 for (int i = 0; i < min.length(); i++) { for (int j = 0, k = min.length() - i; k < min.length() + 1; j++, k++) { String tmp = min.substring(j, k); if (max.contains(tmp)) { return tmp; } } } return null; } public static void main(String agrs[]) { Scanner input = new Scanner(System.in); String s1 = input.next(); String s2 = input.next(); Main ma = new Main(); System.out.println(ma.same(s1, s2)); } }
给你一个字符串 s
、一个字符串 t
。返回 s
中涵盖 t
所有字符的最小子串。如果 s
中不存在涵盖 t
所有字符的子串,则返回空字符串 ""
。
注意:
- 对于
t
中重复字符,我们寻找的子字符串中该字符数量必须不少于t
中该字符数量。 - 如果
s
中存在这样的子串,我们保证它是唯一的答案。
class Solution { int mapt[] =new int[123]; int maps[] =new int[123]; //'z':ASCII 122 public String minWindow(String s, String t) { String smin =""; for(int i=0;i<t.length();i++){ mapt[t.charAt(i)]++; } int sta=0,end=0,minl =Integer.MAX_VALUE,cnt=0;//Integer.MAX_VALUE 2*10^(9)左右 while(end<s.length()){ if(mapt[s.charAt(end)]!=0){ if( mapt[s.charAt(end)] >maps[s.charAt(end)] ) cnt++;//表示得到t中一个字符 maps[s.charAt(end)]++; } if(cnt==t.length()){ while(mapt[s.charAt(sta)]==0||maps[s.charAt(sta)]>mapt[s.charAt(sta)]){//s[sta]可以不用:该字符不在t中或者此时的s子串已经有足够的该字符 if(maps[s.charAt(sta)]>mapt[s.charAt(sta)]){ maps[s.charAt(sta)]--;//多了就减1,表示从子串中去掉该字符 } sta++;//收缩sta } if(minl>end-sta+1){ smin = s.substring(sta,end+1); minl = end-sta+1; } } end++; //后移end } return smin; } }
字符和数字转换
char x = 'b'; int y = x - 'a'; System.out.println(y);//1 int x1 = 3; // x += 96; char y1 = (char) (x1 + 96); System.out.println(y1);//c
String字符串转化为数字
private static String getType(Object a) { return a.getClass().toString(); }
// String--->数字 String s = "056"; int num1 = Integer.parseInt(s); int num2 = Integer.valueOf(s); System.out.println(num1 + " " + num2);// 56 56 String sf = "12.036"; float num3 = Float.parseFloat(sf); double num4 = Double.parseDouble(sf); System.out.println(getType(num3) + ": " + num3 + getType(num4) + ": " + num4);// 12.036 12.036 // class java.lang.Float: 12.036class java.lang.Double: 12.036 // 数字-->String double num = 5036.036; String ss = String.valueOf(num); System.out.println(ss);// 5036.036
字符串拼接用“+” 还是 StringBuilder?
引用的值在程序编译期是无法确定的,编译器无法对其进行优化。不过,字符串使用 final
关键字声明之后,可以让编译器当做常量来处理。
对象引用和“+”的字符串拼接方式,实际上是通过 StringBuilder
调用 append()
方法实现的,拼接完成之后调用 toString()
得到一个 String
对象
意味着每循环一次就会创建一个 StringBuilder
对象。
如果直接使用 StringBuilder
对象进行字符串拼接的话,就不会存在这个问题了。
对于编译期可以确定值的字符串,也就是常量字符串 ,jvm 会将其存入字符串常量池。
并且,字符串常量拼接得到的字符串常量在编译阶段就已经被存放字符串常量池,这个得益于编译器的优化。
常量折叠会把常量表达式的值求出来作为常量嵌在最终生成的代码中,这是 Javac 编译器会对源代码做的极少量优化措施之一
字符串常量池
上述代码,只创建了一个对象
JDK1.7 之前运行时常量池逻辑包含字符串常量池存放在方法区。JDK1.7 的时候,字符串常量池被从方法区拿到了堆中。
// 直接在堆内存空间创建一个新的对象。 String str2 = new String("abcd"); String str3 = new String("abcd"); str2 != str3
只要使用 new 的方式创建对象,便需要创建新的对象 。
使用 new 的方式创建对象的方式如下,可以简单概括为 3 步:
- 在堆中创建一个字符串对象
- 检查字符串常量池中是否有和 new 的字符串值相等的字符串常量
- 如果没有的话需要在字符串常量池中也创建一个值相等的字符串常量,如果有的话,就直接返回堆中的字符串实例对象地址。
因此,str2
和 str3
都是在堆中新创建的对象
String s1 = new String("abc");这句话创建了几个字符串对象?
会创建 1 或 2 个字符串:
- 如果字符串常量池中已存在字符串常量“abc”,则只会在堆空间创建一个字符串常量“abc”。
- 如果字符串常量池中没有字符串常量“abc”,那么它将首先在字符串常量池中创建,然后在堆空间中创建,因此将创建总共 2 个字符串对象。
String s1 = new String("abc");// 堆内存的地址值 String s2 = "abc"; System.out.println(s1 == s2);// 输出 false,因为一个是堆内存,一个是常量池的内存,故两者是不同的。 System.out.println(s1.equals(s2));// 输出 true
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
2019-12-15 分类、逻辑回归
2019-12-15 第一次编程作业