字符串操作 及 String类

n阶魔方阵解法优化

思想:(1-9) 将1放在魔方阵第一行的中间位置,从第二个数字开始放在1第一个数字的上一行下一列,若已经存在数据,则放在上一个数字的下一行,列数不变。

package mypratice;
import java.util.Arrays;
/**
 * @Package: mypratice
 * @Author:kkz
 * @Description:
 * @Date:Created in 2018/10/24 0:34
 */
public class Mofang {
    public static void magicScqure(int[][] array,int n){ 
        array[0][n/2] = 1; //将数字 1 放入魔方阵 
        int prevRow = 0;
        int prevCol = n/2;
        for(int i = 2;i <= n*n;i++) {
        // 注意:判断上一行下一列是否有数据所用的算法,% 求余
            if(array[(prevRow-1+n)%n][(prevCol+1)%n] != 0) {
                //上一行的下一列有数据
                prevRow = (prevRow+1)%n;//放在当前数的下一行
            } else {
                prevRow = (prevRow-1+n)%n; //有数据,则放在当前数的 上一行,下一列
                prevCol = (prevCol+1)%n;
            }
            array[prevRow][prevCol] = i;
        }
    }
    public static void main(String[] args) {
        int row = 3;
        int[][] array = new int[row][row];
        magicScqure(array,row);
        System.out.println(Arrays.deepToString(array));
        }
   }
[[8, 1, 6], [3, 5, 7], [4, 9, 2]]

字符串操作

 java字符串是Unicode字符的有序集合,Unicode字符是使用UTF-16进行编码的。在Java语言中,字符串作为对象,这与在C语言作为字符数组来处理不同。
 java语言使用java.lang包中的String、StringBuilder 和 StringBuffer来构造自定义字符串,执行许多基础字符串操作,如比较字符串、搜索字符串、提取子字符串等。
 注:String、StringBuilder 和 StringBuffer均为密封类,不能派生子类。(密封类:类和方法 被关键字 final 修饰之后,类不能被继承,方法不能被修改。密封类的优点:密封类可以防止有意的派生。)

String类

  String 对象是不可变的(只读),因为一旦创建了该对象,就不能修改对象的值。有些字符串操作看来似乎修改了String对象,实际上返回了一个包含修改内容的新String对象。如果需要修改字符串对象的实际内容,可以使用StringBuilder 或 StringBuffer 类。String类源代码:

 //final 所修饰的类不能改变其值, String类是不可变类
 public final class String
     implements java.io.Serializable, Comparable<String>, CharSequence {
      /** The value is used for character storage.
       private final char value[];  //value[] 数组用于存储字符串
  • 例:1. 声明和初始化字符串,并判断输出结果是否相等:
public class Testdome1024 {
    public static void main(String[] args) {
        String str1 = "hello";    //str1  变量(变量在运行时才可知其值)   hello 字符串常量(常量在编译时统一处理)
        String str2 = new String("hello");  //new 实例化 产生对象
        String str3 = "he" +"llo";  //常量
        String str4 = new String("he") + new String("llo");
        System.out.println(str1 == str2);
        System.out.println(str3 == str1);
        System.out.println(str3 == str2);
        System.out.println(str4 == str3);
    }
}
false
true
false
false
  • 内存分配图如下:
    在这里插入图片描述

谈谈栈与堆的存储:

1.堆(对象):
 引用类型的变量 ,其内存分配在上或者常量池(字符串常量,基本数据类型常量),需要通过new等方式创建。
 堆内存的主要作用是存放运行时new创建的对象。(主要用于存放对象,存取速度慢,可以运行时动态分配内存,生存期不需要提前确定)。
2.栈(基本数据类型变量,对象的引用变量):
 基本数据类型的变量(int、short、long、byte、float、double、boolean、char等)以及对象的引用变量,其内存分配在栈上,变量出了作用域就会自动释放。
 栈内存的主要作用是存放基本数据类型和引用变量。栈的内存管理是通过栈的"后进先出"模式来实现的。(主要用来执行程序,存取速度快,大小和生存期必须确定,缺乏灵活性)

  • 2.运用“+”连接字符串,判断是否相等:
public class Stringa {
    public static void main(String[] args) {
        String str1 = "hello";
        String str2 = "world";
        String str3 = "helloworld";
        System.out.println(str1 + str2);
        System.out.println(str3);
        
        System.out.println(str3 == (str1 + str2));
        System.out.println(str3 == ("hello"+"world"));
    }
}
helloworld
helloworld
false
true
  • 内存分配图如下:
    在这里插入图片描述
     运用jdk自带的工具javap来反编译以上代码,命令为:
    javac src\java文件存储位置   javap -c src.java文件存储位置
    显示字节码如下:
G:\javacode\HelloWorld>javac src\mypratice\Stringa.java

G:\javacode\HelloWorld>javap -c src.mypratice.Stringa
警告: 二进制文件src.mypratice.Stringa包含mypratice.Stringa
Compiled from "Stringa.java"
public class mypratice.Stringa {
  public mypratice.Stringa();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: ldc           #2                  // String hello
       2: astore_1
       3: ldc           #3                  // String world
       5: astore_2
       6: ldc           #4                  // String helloworld
       8: astore_3
       9: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
      12: new           #6                  // class java/lang/StringBuilder
      15: dup
      16: invokespecial #7                  // Method java/lang/StringBuilder."<init>":()V
      19: aload_1
      20: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      23: aload_2
      24: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      27: invokevirtual #9                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      30: invokevirtual #10                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      33: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
      36: aload_3
      37: invokevirtual #10                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      40: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
      43: aload_3
      44: new           #6                  // class java/lang/StringBuilder
      47: dup
      48: invokespecial #7                  // Method java/lang/StringBuilder."<init>":()V
      51: aload_1
      52: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      55: aload_2
      56: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      59: invokevirtual #9                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      62: if_acmpne     69
      65: iconst_1
      66: goto          70
      69: iconst_0
      70: invokevirtual #11                 // Method java/io/PrintStream.println:(Z)V
      73: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
      76: aload_3
      77: ldc           #4                  // String helloworld
      79: if_acmpne     86
      82: iconst_1
      83: goto          87
      86: iconst_0
      87: invokevirtual #11                 // Method java/io/PrintStream.println:(Z)V
      90: return
}

 从生成的字节码文件中可以看出,"+"操作符在代码中的实现过程实际上是编译器自动引用了java.lang.StringBuilder()类,为每一个字符串调用了StringBuilder的append()方法,接下来会具体写到String类的常用方法。

    1. 判断字符串是否相等,第三种情况。
public class Stringa {
    public static void main(String[] args) {
        String str1 = new String("hello");
        String str2 = "hello";
        System.out.println(str1 == str2);//false
        
        String str3 = "he"+new String("llo");
        System.out.println(str1 == str3);//false
        System.out.println(str2 == str3);//false

        String str4 = "he"+"llo";
        System.out.println(str4 == str2);//true

        char[] array = {'h','e','l','l','o'};
        String str5 = new String(array);
        System.out.println("=============");
        System.out.println(str1 == str5);//false
        System.out.println(str2 == str5);//false
        System.out.println(str3 == str5);//false
        System.out.println(str4 == str5);//false
    }
 }
  • 内存分配图如下:
    在这里插入图片描述

String类的常用方法

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
下面实践常用的几个方法:

  1. 字符串空判断:String.isEmpty()
  • 源代码:
public boolean isEmpty() {
        return value.length == 0;
    }
  • 例:
public class Stringa {
    public static void main(String[] args) {
        String str1 ="world";
        str1.length();
        str1.isEmpty();
        System.out.println(str1.length()); //返回字符串长度
        System.out.println(str1.isEmpty());//判断字符串是否为空,如果字符串的length()为0,则返回true
    }
5
false
  1. 获取字符、截取字符串:
    (1)返回指定索引处的字符串:String.ChatAt()
  • 源代码:
 public char charAt(int index) {
        if ((index < 0) || (index >= value.length)) {
            throw new StringIndexOutOfBoundsException(index);
        }
        return value[index];
    }

(2)截取子字符串:(从begindex到结束):String.substring()

  • 源代码:
public String substring(int beginIndex) {
        if (beginIndex < 0) {
            throw new StringIndexOutOfBoundsException(beginIndex);
        }//开始索引小于0,抛出异常
        int subLen = value.length - beginIndex;
        if (subLen < 0) {
            throw new StringIndexOutOfBoundsException(subLen);
        }
        return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
    }

(3)截取部分子字符串(从begindex到endindex):String.substring()

  • 源代码:
 public String substring(int beginIndex, int endIndex) {
        if (beginIndex < 0) {
            throw new StringIndexOutOfBoundsException(beginIndex);
        }//开始索引小于0,抛出异常
        if (endIndex > value.length) {
            throw new StringIndexOutOfBoundsException(endIndex);
        }//结束索引大于字符串长度,抛出异常
        int subLen = endIndex - beginIndex;
        if (subLen < 0) {
            throw new StringIndexOutOfBoundsException(subLen);
        }
        return ((beginIndex == 0) && (endIndex == value.length)) ? this
                : new String(value, beginIndex, subLen);
    }
  • 例:
public class Stringa {
    public static void main(String[] args) {
        String str1 ="world";
     
        System.out.println(str1.charAt(4));
        System.out.println(str1.substring(0));//截取整个字符串
        System.out.println(str1.substring(0,3));//截取部分字符串
        }
}
d
world
wor
  1. (1)拷贝整个char数组给目标数组:String.getChars()
  • 源代码:
  //拷贝整个数组给dst 数组
    public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) { //起始索引,结束索引,目标数组,目标数组起始索引
        if (srcBegin < 0) {
            throw new StringIndexOutOfBoundsException(srcBegin);
        }
        if (srcEnd > value.length) {
            throw new StringIndexOutOfBoundsException(srcEnd);
        }
        if (srcBegin > srcEnd) {
            throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
        }
        System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin); //源数组,起始索引,目的数组,目的数组起始索引,源数组拷贝长度
    }  //底层调用的是System。arraycopy()方法

(2)复制byte到目标数组:String.getBytes()

@Deprecated        注解
    //已经过时的 方法          调用时会有横线划掉
    public void getBytes(int srcBegin, int srcEnd, byte dst[], int dstBegin) {
        if (srcBegin < 0) {
            throw new StringIndexOutOfBoundsException(srcBegin);
        }
        if (srcEnd > value.length) {
            throw new StringIndexOutOfBoundsException(srcEnd);
        }
        if (srcBegin > srcEnd) {
            throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
        }
        Objects.requireNonNull(dst);

        int j = dstBegin;
        int n = srcEnd;
        int i = srcBegin;
        char[] val = value;   /* avoid getfield opcode */

        while (i < n) {
            dst[j++] = (byte)val[i++];
        }
    }
  • 例:
public class Stringa {
    public static void main(String[] args) {
        String str1 ="myprogrammingworld";
      
        char[] a2 = new char[15];
        str1.getChars(0,12,a2,0);
        System.out.println(a2); //拷贝char到目标数组
        byte[] a3 = new byte[10];
        str1.getBytes(0,10,a3,0); //已经过时的方法,使用时会有划线提示
        System.out.println(a3);//拷贝byte到目标数组
        }
 }
myprogrammin   
[B@154617c
  1. 比较字符串:String.equals()
          String.compareTo()
    注:equals、compareTo方法用于比较字符串内容,“==”运算符用于比较对象。
  • 源代码:
public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
		//anObject是否为String的实例
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }
  1. 为每个唯一字符序列生成一个且仅生成一个String引用:String.intern()
  • 源代码:
public native String intern();//intern():如果在常量池当中没有字符串的引用,那么就会生成一个在常量池当中的引用;相反:则不生成
  • 例:
public class Testdome1024 {
    public static void main1(String[] args) {
        String str1 = new String("ab")+new String("cdef");
        //str1.intern();  //true
        String str2 = "abcdef";
        str1.intern();
        System.out.println(str1 == str2);//false
    }

    public static void main2(String[] args) {
        String string = new String("abcdef");
        string.intern();
        String string2 = "abcdef";
        System.out.println(string == string2);//false
    }

    public static void main(String[] args) {
        String string = new String("abcdef").intern();
        String string2 = "abcdef";
        System.out.println(string == string2);//true
    }
}
  • 理解:
        String str1 = new String("ab")+new String("cdef");
        str1.intern();  
        String str2 = "abcdef";
        System.out.println(str1 == str2);//true

在JDK 1.7下,当执行str1.intern();时,因为常量池中没有“abcdef”这个字符串,所以会在常量池中生成一个对堆中的“abcdef”的引用(注意这里是引用 ,就是这个区别于JDK 1.6的地方。在JDK1.6下是生成原字符串的拷贝),而在进行String str2 = “abcdef”;字面量赋值的时候,常量池中已经存在一个引用,所以直接返回了该引用,因此str1和str2都指向堆中的同一个字符串,返回true。

        String str1 = new String("ab")+new String("cdef");
        String str2 = "abcdef";
        str1.intern();
        System.out.println(str1 == str2);//false

将中间两行调换位置以后,因为在进行字面量赋值(String str2 = “abcdef″)的时候,常量池中不存在,所以str2指向的常量池中的位置,而str1指向的是堆中的对象,再进行intern方法时,对str1和str2已经没有影响了,所以返回false。

  • 内存分配:
    在这里插入图片描述
  1. String replace(CharSequence oldString,CharSequence newString):
    返回一个新字符串。这个字符串用newString代替原始字符串中的所有oldString。可以用String或StringBuilder对象作为CharSequence参数。
  2. boolean startsWith(String predix):
    如果字符串以preffix 字符串开始,返回true。
  3. String toLowerCase()::返回一个新的字符串,这个字符串将原始字符串中的所有大写字母改写成了小写字母。
  4. String toUpperCase():返回一个新的字符串,这个字符串将原始字符串中的小写字母改成了大写字母。
  5. String trim(): 返回一个新的字符串,这个字符串将删除了原始字符串头部和尾部的空格。

StringBuilder 和 StringBuffer

 字符串(String)对象是不可变的,即它们在创建之后就无法更改。对字符串进行操作,要在内存中创建一个新的字符串对象,这就需要为该新对象分配新的空间。
String s1 = “Hello”;
String s2 = s1;
s1 += “and goodbye”; //创建新的字符串对象
System.out.println(s2);
 如果需要对字符串执行重复修改,则与创建新的String对象相关的系统开销可能会非常昂贵。如果要修改字符串而不创建新的对象,则可以使用StringBuilder或StringBuffer类。
 StringBuilder 类和StringBuffer类都表示值为可变字符序列的类似字符串的对象,可通过追加、移除、替换或插入字符创建它后对它进行修改。二者的API完全兼容。
 StringBuffer类是线程安全类,可安全地用于多个线程;StringBuilder类则是StringBuffer的一个简易替换,用在字符串缓冲区被单线程使用的时候。相比StringBuffer,StringBuilder具有更高的性能。
StringBuffer

  • 多线程使用
  • StringBuffer.append ("") 追加 append返回当前的对象
  • synchronized 关键字 保护线程 线程安全
  • 单线程 多线程 线程池
  • StringBuilder String 单线程使用
    ** 练习 **
    1.左旋数组:“abcdef”==>“cdefab” 参数 String 位置 n
    编写函数,将字符串从左数第n个开始进行旋转(也叫左旋数组)。
  • 思路: 例如:String str = “abcdef”;从第二个开始旋转,旋转后的结果为:”cdefab”;可以先将左旋前面的部分字符串逆置得到:“bacdef”; 再将后面部分字符串逆置得到:”bafedc”;最后整体再逆置一次得到:”cdefab”;就完成了。
public class LeftArray {
    public static String  leftarray(String str,int n){
        if(str == null || n > str.length()) {
            return null;
        }
        int left1 = 0;
        int right1 = n-1;
        int left2 = n;
        int right2 = str.length()-1;
        str = reverse(str,left1,right1);//左旋前面的部分字符串逆置   得到ba  cdef
        str = reverse(str,left2,right2);//后面部分字符串逆置     得到   ba  fedc
        str = reverse(str,left1,right2);//整体再逆置     得到  cdefab
        return str;
    }
    private static String reverse(String str, int start, int end) {
        char ch[] = str.toCharArray();//将字符串转成字符数组
        char tmp;
        while(start < end) {
            tmp = ch[start];
            ch[start] = ch[end];
            ch[end] = tmp;
            start++;
            end--;
        }
        return String.copyValueOf(ch);//将字符数组转成字符串
    }
    public static void main(String[] args) {
        String str = "abcdef";
        System.out.println("原字符串为:"+str);
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入从第几个元素开始左旋");
        int n = sc.nextInt();
        System.out.println("左旋后字符串为:"+ leftarray(str,n));
    }
}

原字符串为:abcdef
请输入从第几个元素开始左旋
2
左旋后字符串为:cdefab

猜你喜欢

转载自blog.csdn.net/nn1__2970/article/details/83384359