String、StringBuffer和StringBuilder的区别
参考链接:https://www.cnblogs.com/weibanggang/p/9455926.html
https://www.cnblogs.com/onetheway2018/p/11553168.html
运行速度
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常被用于用户名、密码、文件名等使用,由于其不可变,可避免黑客行为对其恶意修改。
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?
对象引用和“+”的字符串拼接方式,实际上是通过 StringBuilder
调用 append()
方法实现的,拼接完成之后调用 toString()
得到一个 String
对象
意味着每循环一次就会创建一个 StringBuilder
对象。
如果直接使用 StringBuilder
对象进行字符串拼接的话,就不会存在这个问题了。