java字符串替换算法_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对象

23eccf30f746d555d19f4b1f4b8ebcb5.png

两种方式是有区别的,这和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

afffd112ccec3870a54ba5821137d143.png

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

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

面试。。。。

面试题4:替换空格

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

它原先的想法是从头开始遍历,遇到空格就替换,这样的话后面的字符就会移动,有些字符会移动多次,算法效率较低,时间复杂度为O(n^2)

所以提出一种新的想法,就是从后面开始遍历,先算出替换之后的长度是多少,然后用一个指针A指向那个地方,另一个指针B指向原来字符数组的末尾,然后一个一个复制过去,当B遇到空格的时候,在A那里添加“%20”。

大概思路就是这样子。

但是问题就出在这里,字符数组的长度是不可变的,至少在java中是这样,所以我们要新建一个数组吗?

但是新建一个数组的话,我们只要从头开始一个一个遍历,然后遇到空格就在新数组中加入“%20”,这样时间复杂度不也是O(n)吗

那这道题不就没有意义了?

所以我想问的是 大家觉得剑指offer的题都适合用java做吗?

错误的想法如下:

7ac293b3c99085472f9b5426881a73e1.png

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

e49394523042a8cbcaef0b0b3c1fc937.png

C语言代码

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 //ReplaceBlank.cpp : Defines the entry point for the console application.2 //

3

4 //《剑指Offer——名企面试官精讲典型编程题》代码5 //著作权所有者:何海涛

6

7 #include "stdafx.h"

8 #include

9

10 /*length 为字符数组string的总容量*/

11 void ReplaceBlank(char string[], intlength)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] != '\0')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, charexpected[])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.\n");63 else if(expected == NULL && string !=NULL)64 printf("failed.\n");65 else if(strcmp(string, expected) == 0)66 printf("passed.\n");67 else

68 printf("failed.\n");69 }70

71 //空格在句子中间

72 voidTest1()73 {74 const int length = 100;75

76 char string[length] = "hello world";77 Test("Test1", string, length, "hello%20world");78 }79

80 //空格在句子开头

81 voidTest2()82 {83 const int length = 100;84

85 char string[length] = "helloworld";86 Test("Test2", string, length, "%20helloworld");87 }88

89 //空格在句子末尾

90 voidTest3()91 {92 const int length = 100;93

94 char string[length] = "helloworld";95 Test("Test3", string, length, "helloworld%20");96 }97

98 //连续有两个空格

99 voidTest4()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 voidTest5()109 {110 Test("Test5", NULL, 0, NULL);111 }112

113 //传入内容为空的字符串

114 voidTest6()115 {116 const int length = 100;117

118 char string[length] = "";119 Test("Test6", string, length, "");120 }121

122 //传入内容为一个空格的字符串

123 voidTest7()124 {125 const int length = 100;126

127 char string[length] = " ";128 Test("Test7", string, length, "%20");129 }130

131 //传入的字符串没有空格

132 voidTest8()133 {134 const int length = 100;135

136 char string[length] = "helloworld";137 Test("Test8", string, length, "helloworld");138 }139

140 //传入的字符串全是空格

141 voidTest9()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

转载:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值