java 字符串

String、StringBuffer和StringBuilder的区别

参考链接:https://www.cnblogs.com/weibanggang/p/9455926.html

https://www.cnblogs.com/onetheway2018/p/11553168.html

运行速度

StringBuilder > StringBuffer > String

String 真正不可变有下面几点原因:

  1. 保存字符串的数组(字符数组)被 final 修饰且为私有的,并且String 类没有提供/暴露修改这个字符串的方法。
  2. 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设计为不变量?

  1. 节省空间:字符串常量存储在JVM的字符串池中可以被用户共享。
  2. 提高效率:String会被不同线程共享,是线程安全的。在涉及多线程操作中不需要同步操作。
  3. 安全: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 对象进行字符串拼接的话,就不会存在这个问题了。

 

字符串常量池

JDK1.7 之前运行时常量池逻辑包含字符串常量池存放在方法区。JDK1.7 的时候,字符串常量池被从方法区拿到了堆中。

 

原文地址:https://www.cnblogs.com/tingtin/p/15693192.html