• Java中的String类和算法例子替换空格


    在java中,说String是不可变的,可是为什么

    当String变量需要经常变换其值时,应该考虑使用StringBuffer类,以提高程序效率?
    假设String s=new String ("wo");String s1=new String("de");
    s=s+s1;
    System.out.println(s);结果为wode?
    首先在栈中有个"s"变量指向堆中的"wo"对象...
    栈中"s1"变量指向堆中的"de"对象
    当执行到s = s + s1;
    系统重新在堆中new一个更大的数组出来,然后将"wo"和"de"都复制进去,然后栈中的"s"指向这个新new出来的数组...
    所谓的不可变是指:它没有在原数组“wo”上进行修改,而是新建了个更大数组进行扩展,
    也就是说,这时候堆里还是有“wo”这个对象数组存在的,只不过这个时候"s"变量不在指向"wo"这个数组了,
    而是指向了新new出来的数组,这就是和StringBuffered的区别,后者是在原数组上进行修改,改变了原数组的值,
    StringBuffered不是通过新new一个数组去复制,而是在原数组基础上进行扩展...再让变量指向原数组....

    不再纠结Java中的String类

      String是我们经常用到的一个类型,其实有时候觉得写程序就是在反复的操作字符串,这是C的特点,

    在java中,jdk很好的封装了关于字符串的操 作。

    今天主要讲的是三个类String 、StringBuffer 、 StringBuilder .

    这三个类基本上满足了我们在不同情景下使用字符串的需求。

      先说,第一个String。

      JDK的解释是 “Strings are constant; their values cannot be changed after they are created”

    也就是说String对象一旦被创建就是固定不变的了(你一定有问题,但请先等一等,耐心读下去),

    这样的一点好处就是可以多线程之间访 问,因为只读不写。

      一般情况下我们以下面两种方式创建一个String对象    

      

      两种方式是有区别的,这和java的内存管理有关,前面已经说过,string创建之后是不可变的,

    所以按照第一种方式创建的字符串会放在栈里,更确切的是常量池中,常量池就是用来保存在编译阶段确定好了大小的数据,

    一般我们定义的int等基本数据类型就保存在这里。

      其具体的一个流程就是,编译器首先检查常量池,看看有没有一个“string”,如果没有则创建。如果有的话,

    则则直接把str1指向那个位置。

      第二种创建字符串的方法是通过new关键字,还是java的内存分配,java会将new的对象放在堆中,这一部分

    对象是在运行时创建的对象。所以我们每一次new的时候,都会创建不同的对象,即便是堆中已经有了一个一模

    一样的。

    写一个小例子

     1         String str1 = "string";  
     2             String str4 = "string";  
     3             String str2 = new String("string");  
     4             String str3 = new String("string");  
     5               
     6             /*用于测试两种创建字符串方式的区别*/ 
     7             System.out.println(str1 == str4);  
     8             System.out.println(str2 == str3);  
     9             System.out.println(str3 == str1);  
    10               
    11             str3 = str3.intern(); //一个不常见的方法  
    12             System.out.println(str3 == str1); 

    这个的运行结果是

    true //解释:两个字符串的内容完全相同,因而指向常量池中的同一个区域

    false //解释:每一次new都会创建一个新的对象

    false // 解释: 注意==比较的是地址,不仅仅是内容

    true //介绍一下intern方法,这个方法会返回一个字符串在常量池中的一个地址,如果常量池中有与str3内容相

    同的string则返回那个地址,如果没有,则在常量池中创建一个string后再返回。实际上,str3现在指向了str1

    的地址。

      这就是让人纠结的string了,现在你可以说话了。。。很多人有这样的疑问就是既然string是不变的,那么

    为什么str1 + "some"是合法的,其实,每次对string进行修改,都会创建一个新的对象。

      所以如果需要对一个字符串不断的修改的话,效率是非常的低的,因为堆的好处是可以动态的增加空间,劣

    势就是分配新的空间消耗是很大的,比如我们看下面的测试。

    1     long start = System.currentTimeMillis();  
    2               
    3             for(int i = 0; i < 50000; i++)  
    4             {  
    5                 str1+= " ";  
    6             }  
    7               
    8             long end = System.currentTimeMillis();  
    9             System.out.println("the run time is "+(end -start)+" ms"); 

      我的机器上运行结果是the run time is 3538 ms 如果你把循环的次数后面再增加几个0就会更慢。因为每

    一次循环都在创建心的对象,那么JDK如何解决这个问题?

      下面就要说第二个类StringBuffer。

      StringBuffer是一个线程安全的,就是多线程访问的可靠保证,最重要的是他是可变的,也就是说我们要操

    作一个经常变化的字符串,可以使用 这个类,基本的方法就是append(与string的concat方法对应)和insert

    方法,至于怎么使用,就不多讲了,大家可以自己查看API。

     1     StringBuilder sb = new StringBuilder("string builder");  
     2             StringBuffer sf = new StringBuffer("string buffer");  
     3               
     4             long start = System.currentTimeMillis();  
     5               
     6             for(int i = 0; i < 50000; i++)  
     7             {  
     8                 //str1+= " ";  
     9                 sb.append(" ");  
    10             }  
    11               
    12             long end = System.currentTimeMillis();  
    13             System.out.println("the run time is "+(end -start)+" ms"); 

      测试一下,这次只需要8ms,这就是效率。

      那么接下来,就要问StringBuilder是干什么的,其实这个才是我们尝使用的,这个就是在jdk 1.5版本后面

    添加的新的类,前面说StringBuffer是线程同步的,那么很多情况下,我们只是使用一个线程,那个同步势必带

    来一个效率的问 题,StringBuilder就是StringBuffer的非线程同步的版本,二者的方法差不多,只是一个线程

    安全(适用于多线程)一个没有线程安全 (适用于单线程)。

      其实看了一下jdk源代码就会发现,StringBuffer就是在各个方法上加上了关键字syncronized

      以上就是对三个字符串类的一个总结,总之不要在这上面纠结。。。。。。不想介绍太多的方法,总觉得那

    样会把一篇博客弄成API文档一样,而且还非常的繁琐。都是些体会,希望有所帮助。起码不要再纠结,尤其是

    面试。。。。

    面试题4:替换空格

    请实现一个函数,将一个字符串中的空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy.

    它原先的想法是从头开始遍历,遇到空格就替换,这样的话后面的字符就会移动,有些字符会移动多次,算法效率较低,时间复杂度为O(n^2) 
    所以提出一种新的想法,就是从后面开始遍历,先算出替换之后的长度是多少,然后用一个指针A指向那个地方,另一个指针B指向原来字符数组的末尾,然后一个一个复制过去,当B遇到空格的时候,在A那里添加“%20”。
    大概思路就是这样子。
    但是问题就出在这里,字符数组的长度是不可变的,至少在java中是这样,所以我们要新建一个数组吗?
    但是新建一个数组的话,我们只要从头开始一个一个遍历,然后遇到空格就在新数组中加入“%20”,这样时间复杂度不也是O(n)吗
    那这道题不就没有意义了?
    所以我想问的是 大家觉得剑指offer的题都适合用java做吗?

    错误的想法如下:

    正确的想法,但是StringBuffer是如何工作的?

    C语言代码

      1 // ReplaceBlank.cpp : Defines the entry point for the console application.
      2 //
      3 
      4 // 《剑指Offer——名企面试官精讲典型编程题》代码
      5 // 著作权所有者:何海涛
      6 
      7 #include "stdafx.h"
      8 #include <string>
      9 
     10 /*length 为字符数组string的总容量*/
     11 void ReplaceBlank(char string[], int length)
     12 {
     13     if(string == NULL && length <= 0)
     14         return;
     15 
     16     /*originalLength 为字符串string的实际长度*/
     17     int originalLength = 0;
     18     int numberOfBlank = 0;
     19     int i = 0;
     20     while(string[i] != '')
     21     {
     22         ++ originalLength;
     23 
     24         if(string[i] == ' ')
     25             ++ numberOfBlank;
     26 
     27         ++ i;
     28     }
     29 
     30     /*newLength 为把空格替换成'%20'之后的长度*/
     31     int newLength = originalLength + numberOfBlank * 2;
     32     if(newLength > length)
     33         return;
     34 
     35     int indexOfOriginal = originalLength;
     36     int indexOfNew = newLength;
     37     while(indexOfOriginal >= 0 && indexOfNew > indexOfOriginal)
     38     {
     39         if(string[indexOfOriginal] == ' ')
     40         {
     41             string[indexOfNew --] = '0';
     42             string[indexOfNew --] = '2';
     43             string[indexOfNew --] = '%';
     44         }
     45         else
     46         {
     47             string[indexOfNew --] = string[indexOfOriginal];
     48         }
     49 
     50         -- indexOfOriginal;
     51     }
     52 }
     53 
     54 void Test(char* testName, char string[], int length, char expected[])
     55 {
     56     if(testName != NULL)
     57         printf("%s begins: ", testName);
     58 
     59     ReplaceBlank(string, length);
     60 
     61     if(expected == NULL && string == NULL)
     62         printf("passed.
    ");
     63     else if(expected == NULL && string != NULL)
     64         printf("failed.
    ");
     65     else if(strcmp(string, expected) == 0)
     66         printf("passed.
    ");
     67     else
     68         printf("failed.
    ");
     69 }
     70 
     71 // 空格在句子中间
     72 void Test1()
     73 {
     74     const int length = 100;
     75 
     76     char string[length] = "hello world";
     77     Test("Test1", string, length, "hello%20world");
     78 }
     79 
     80 // 空格在句子开头
     81 void Test2()
     82 {
     83     const int length = 100;
     84 
     85     char string[length] = " helloworld";
     86     Test("Test2", string, length, "%20helloworld");
     87 }
     88 
     89 // 空格在句子末尾
     90 void Test3()
     91 {
     92     const int length = 100;
     93 
     94     char string[length] = "helloworld ";
     95     Test("Test3", string, length, "helloworld%20");
     96 }
     97 
     98 // 连续有两个空格
     99 void Test4()
    100 {
    101     const int length = 100;
    102 
    103     char string[length] = "hello  world";
    104     Test("Test4", string, length, "hello%20%20world");
    105 }
    106 
    107 // 传入NULL
    108 void Test5()
    109 {
    110     Test("Test5", NULL, 0, NULL);
    111 }
    112 
    113 // 传入内容为空的字符串
    114 void Test6()
    115 {
    116     const int length = 100;
    117 
    118     char string[length] = "";
    119     Test("Test6", string, length, "");
    120 }
    121 
    122 //传入内容为一个空格的字符串
    123 void Test7()
    124 {
    125     const int length = 100;
    126 
    127     char string[length] = " ";
    128     Test("Test7", string, length, "%20");
    129 }
    130 
    131 // 传入的字符串没有空格
    132 void Test8()
    133 {
    134     const int length = 100;
    135 
    136     char string[length] = "helloworld";
    137     Test("Test8", string, length, "helloworld");
    138 }
    139 
    140 // 传入的字符串全是空格
    141 void Test9()
    142 {
    143     const int length = 100;
    144 
    145     char string[length] = "   ";
    146     Test("Test9", string, length, "%20%20%20");
    147 }
    148 
    149 int _tmain(int argc, _TCHAR* argv[])
    150 {
    151     Test1();
    152     Test2();
    153     Test3();
    154     Test4();
    155     Test5();
    156     Test6();
    157     Test7();
    158     Test8();
    159     Test9();
    160 
    161     return 0;
    162 }
    View Code

    转载:

    http://developer.51cto.com/art/201204/327109.htm

    http://bbs.csdn.net/topics/390869449

  • 相关阅读:
    图论一角
    入门-k8s部署应用 (三)
    入门-k8s集群环境搭建(二)
    入门-Kubernetes概述 (一)
    shell中获取时间
    Linux shell脚本之 if条件判断 (转)
    linux中shell变量$#等的释义
    shell 的here document 用法 (cat << EOF) (转)
    Homebrew的安装与使用
    docker容器编排 (4)
  • 原文地址:https://www.cnblogs.com/Mokaffe/p/4315901.html
Copyright © 2020-2023  润新知