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 真正不可变有下面几点原因:

 

  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常被用于用户名、密码、文件名等使用,由于其不可变,可避免黑客行为对其恶意修改。

 

 

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

    }

}

 

      String str = "helloworld";

        String s1 = str.replaceFirst("l", "");//删除一个l
        String s2 = s1.replaceFirst("l", "");//在删除一个l
        System.out.println(s1);
        System.out.println(s2);
 

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 ans = new StringBuilder();
        ans.append("dfvf");
        ans.reverse();
        System.out.println(ans);
 
fvfd

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 步:

  1. 在堆中创建一个字符串对象
  2. 检查字符串常量池中是否有和 new 的字符串值相等的字符串常量
  3. 如果没有的话需要在字符串常量池中也创建一个值相等的字符串常量,如果有的话,就直接返回堆中的字符串实例对象地址。

因此,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

 

posted on 2021-12-15 15:58  cltt  阅读(39)  评论(0编辑  收藏  举报

导航