OMG,12 个精致的 Java 字符串操作小技巧,学它

字符串既然最常用 , 那就意味着面试官好这一口 , 就喜欢问一些字符串方面的编码技巧 , 来测试应聘者是否技术过硬 , 底子扎实 , 对吧?
那这次 , 我就来盘点12个精致的Java字符串操作小技巧 , 来帮助大家提高一下下 。 在查看我给出的答案之前 , 最好自己先动手尝试一遍 , 写不出来答案没关系 , 先思考一遍 , 看看自己的知识库里是不是已经有解决方案 , 有的话 , 就当是温故复习了 , 没有的话 , 也不要担心 , 刚好学一遍 。
01、如何在字符串中获取不同的字符及其数量?这道题可以拆解为两个步骤 , 第一步 , 找出不同的字符 , 第二步 , 统计出它们的数量 。 好像有点废话 , 是不是?那我先来一个答案吧 。
publicclassDistinctCharsCount{publicstaticvoidmain(String[]args){printDistinctCharsWithCount("itwanger");printDistinctCharsWithCount("chenmowanger");}privatestaticvoidprintDistinctCharsWithCount(Stringinput){MapcharsWithCountMap=newLinkedHashMap<>();for(charc:input.toCharArray()){IntegeroldValue=https://pcff.toutiao.jxnews.com.cn/p/20200831/charsWithCountMap.get(c);intnewValue=(oldValue==null)?1:Integer.sum(oldValue,1);charsWithCountMap.put(c,newValue);}System.out.println(charsWithCountMap);}}程序输出的结果是:
{i=1,t=1,w=1,a=1,n=1,g=1,e=1,r=1}{c=1,h=1,e=2,n=2,m=1,o=1,w=1,a=1,g=1,r=1}说一下我的思路:
1)声明一个LinkedHashMap , 也可以用HashMap , 不过前者可以保持字符串拆分后的顺序 , 结果看起来更一目了然 。
为什么要用Map呢?因为Map的key是不允许重复的 , 刚好可以对重复的字符进行数量的累加 。
2)把字符串拆分成字符 , 进行遍历 。
3)如果key为null的话 , 就表明它的数量要+1;否则的话 , 就在之前的值上+1 , 然后重新put到Map中 , 这样就覆盖了之前的字符数量 。
思路很清晰 , 对不对?忍不住给自己鼓个掌 。
那 , JDK8之后 , Map新增了一个很厉害的方法merge() , 一次性为多个键赋值:
privatestaticvoidprintDistinctCharsWithCountMerge(Stringinput){MapcharsWithCountMap=newLinkedHashMap<>();for(charc:input.toCharArray()){charsWithCountMap.merge(c,1,Integer::sum);}System.out.println(charsWithCountMap);}有没有很厉害?一行代码就搞定 。 第一个参数为键 , 第二个参数为值 , 第三个参数是一个BiFunction , 意思是 , 如果键已经存在了 , 就重新根据BiFunction计算新的值 。
如果字符是第一次出现 , 就赋值为1;否则 , 就把之前的值sum1 。
02、如何反转字符串?如果同学们对StringBuilder和StringBuffer很熟悉的话 , 这道题就很简单 , 直接reverse()就完事 , 对不对?
publicclassReverseAString{publicstaticvoidmain(String[]args){reverseInputString("沉默王二");}privatestaticvoidreverseInputString(Stringinput){StringBuildersb=newStringBuilder(input);Stringresult=sb.reverse().toString();System.out.println(result);}}输出结果如下所示:
二王默沉多说一句 , StringBuffer和StringBuilder很相似 , 前者是同步的 , 所有public方法都加了synchronized关键字 , 可以在多线程中使用;后者是不同步的 , 没有synchronized关键字 , 所以性能更佳 , 没有并发要求的话 , 就用StringBuilder 。
03、如何判断一个字符串是前后对称的?什么意思呢?就好像一个字符串 , 前后一折 , 是对称的 。 就像你站在镜子前 , 看到了一个玉树临风、闭月羞花的自己 。
publicclassPalindromeString{publicstaticvoidmain(String[]args){checkPalindromeString("沉默王二");checkPalindromeString("沉默王二二王默沉");}privatestaticvoidcheckPalindromeString(Stringinput){booleanresult=true;intlength=input.length();for(inti=0;i输出结果如下所示:
沉默王二对称吗?false沉默王二二王默沉对称吗?true说一下我的思路:要判断字符串对折后是否对称 , 很简单 , 从中间劈开 , 第一个字符对照最后一个字符 , 一旦找到不等的那个 , 就返回false 。
注意三点:
1)for循环的下标从0开始 , 到length/2结束 。
2)下标i和length-i-1是对称的 。
3)一旦false就break 。
04、如何删除所有出现的指定字符?字符串类没有提供remove()方法 , 但提供了replaceAll()方法 , 通过将指定的字符替换成空白字符就可以办得到 , 对吧?
publicclassRemoveCharFromString{publicstaticvoidmain(String[]args){removeCharFromString("沉默王二",'二');removeCharFromString("chenmowanger",'n');}privatestaticvoidremoveCharFromString(Stringinput,charc){Stringresult=input.replaceAll(String.valueOf(c),"");System.out.println(result);}}输出结果如下所示:
沉默王chemowager05、如何证明字符串是不可变的?字符串不可变的这个事我曾写过两篇文章 , 写到最后我都要吐了 。 但是仍然会有一些同学弄不明白 , 隔段时间就有人私信我 , 我就不得不把之前的文章放到收藏夹 , 问的时候我就把链接发给他 。
之所以造成这个混乱 , 有很多因素 , 比如说 , Java到底是值传递还是引用传递?字符串常量池是个什么玩意?
这次又不得不谈 , 虽然烦透了 , 但仍然要证明啊!
publicclassStringImmutabilityTest{publicstaticvoidmain(String[]args){Strings1="沉默王二";Strings2=s1;System.out.println(s1==s2);s1="沉默王三";System.out.println(s1==s2);System.out.println(s2);}}输出结果如下所示:
truefalse沉默王二1)Strings1="沉默王二" , Java在字符串常量池中创建“沉默王二”这串字符的对象 , 并且把地址引用赋值给s1
2)Strings2=s1 , s2和s1指向了同一个地址引用——常量池中的那个“沉默王二” 。
所以 , 此时s1==s2为true 。
3)s1="沉默王三" , Java在字符串常量池中创建“沉默王三”这串字符的对象 , 并且把地址引用赋值给s1 , 但s2仍然指向的是“沉默王二”那串字符对象的地址引用 。
所以 , 此时s1==s2为false , s2的输出结果为“沉默王二”就证明了字符串是不可变的 。
06、如何统计字符串中的单词数?这道题呢?主要针对的是英文字符串的情况 。 虽然中文字符串中也可以有空白字符 , 但不存在单词这一说 。
publicclassCountNumberOfWordsInString{publicstaticvoidmain(String[]args){countNumberOfWords("MynameisWanger");countNumberOfWords("ILoveJavaProgramming");countNumberOfWords("Javaisveryimportant");}privatestaticvoidcountNumberOfWords(Stringline){StringtrimmedLine=line.trim();intcount=trimmedLine.isEmpty()?0:trimmedLine.split("s+").length;System.out.println(count);}}输出结果如下所示:
444split()方法可以对字符串进行拆分 , 参数不仅可以是空格 , 也可以使正则表达式代替的空白字符(多个空格、制表符);返回的是一个数组 , 通过length就可以获得单词的个数了 。
如果对split()方法很感兴趣的话 , 可以查看我之前写的一篇文章 , 很饱满 , 很丰富 。
咦 , 拆分个字符串都这么讲究
07、如何检查两个字符串中的字符是相同的?如何理解这道题呢?比如说 , 字符串“沉默王二”和“沉王二默”就用了同样的字符 , 对吧?比如说 , 字符串“沉默王二”和“沉默王三”用的字符就不同 , 理解了吧?
publicclassCheckSameCharsInString{publicstaticvoidmain(String[]args){sameCharsStrings("沉默王二","沉王二默");sameCharsStrings("沉默王二","沉默王三");}privatestaticvoidsameCharsStrings(Strings1,Strings2){Setset1=s1.chars().mapToObj(c->(char)c).collect(Collectors.toSet());System.out.println(set1);Setset2=s2.chars().mapToObj(c->(char)c).collect(Collectors.toSet());System.out.println(set2);System.out.println(set1.equals(set2));}}输出结果如下所示:
[默,沉,王,二][默,沉,王,二]true[默,沉,王,二][默,沉,三,王]false上面的代码用到了Stream流 , 看起来很陌生 , 但很好理解 , 就是把字符串拆成字符 , 然后收集到Set中 , Set是一个不允许有重复元素的集合 , 所以就把字符串中的不同字符收集起来了 。
08、如何判断一个字符串包含了另外一个字符串?这道题有点简单 , 对吧?上一道还用Stream流 , 这道题就直接送分了?不用怀疑自己 , 就用字符串类的contains()方法 。
publicclassStringContainsSubstring{publicstaticvoidmain(String[]args){Strings1="沉默王二";Strings2="沉默";System.out.println(s1.contains(s2));}}输出结果如下所示:
truecontains()方法内部其实调用的是indexOf()方法:
publicbooleancontains(CharSequences){returnindexOf(s.toString())>=0;}09、如何在不用第三个变量的情况下交换两个字符串?这道题就有点意思了 , 对吧?尤其是前提条件 , 不使用第三个变量 。
publicclassSwapTwoStrings{publicstaticvoidmain(String[]args){Strings1="沉默";Strings2="王二";s1=s1.concat(s2);s2=s1.substring(0,s1.length()-s2.length());s1=s1.substring(s2.length());System.out.println(s1);System.out.println(s2);}}输出结果如下所示:
王二沉默说一下我的思路:
1)通过concat()方法把两个字符串拼接到一块 。
2)然后通过substring()方法分别取出第二个字符串和第一个字符串 。
10、如何从字符串中找出第一个不重复的字符?来 , 上个例子来理解一下这道题 。 比如说字符串“沉默王沉沉默二” , 第一个不重复的字符是“王” , 对吧?因为“沉”重复了 , “默”重复了 。
publicclassFindNonRepeatingChar{publicstaticvoidmain(String[]args){System.out.println(printFirstNonRepeatingChar("沉默王沉沉默二"));System.out.println(printFirstNonRepeatingChar("沉默王沉"));System.out.println(printFirstNonRepeatingChar("沉沉沉"));}privatestaticCharacterprintFirstNonRepeatingChar(Stringstring){char[]chars=string.toCharArray();ListdiscardedChars=newArrayList<>();for(inti=0;i输出结果如下所示:
王默null说一下我的思路:
1)把字符串拆分成字符数组 。
2)声明一个List , 把重复的字符放进去 。
3)外层的for循环 , 从第一个字符开始 , 如果已经在List中 , 继续下一轮 。
4)嵌套的for循环 , 从第一个字符的下一个字符(j=i+1)开始遍历 , 如果找到和之前字符重复的 , 就加入到List中 , 跳出内层的循环;如果找到最后(j==chars.length-1)也没有找到 , 就是第一个不重复的字符 , 对吧?
11、如何检查字符串中只包含数字?有一种很傻的解法 , 就是用Long.parseLong(string)对字符串强转 , 如果转不成整形 , 那肯定不是只包含数字 , 对吧?
但这种方法也太不可取了 , 所以还得换一种巧妙的 , 就是使用正则表达式 。
publicclassCheckIfStringContainsDigitsOnly{publicstaticvoidmain(String[]args){digitsOnlyString("123沉默王二");digitsOnlyString("123");}privatestaticvoiddigitsOnlyString(Stringstring){if(string.matches("d+")){System.out.println("只包含数字的字符串:"+string);}}}输出结果如下所示:
只包含数字:12312、如何实现字符串的深度拷贝?由于字符串是不可变的 , 所以可以直接使用“=”操作符将一个字符串拷贝到另外一个字符串 , 并且互不影响 。
publicclassJavaStringCopy{publicstaticvoidmain(Stringargs[]){Stringstr="沉默王二";StringstrCopy=str;str="沉默王三";System.out.println(strCopy);}}输出结果如下所示:
沉默王二这个例子和之前证明字符串是不可变的例子几乎没什么差别 , 对吧?这的确是因为字符串是不可变的 , 如果是可变对象的话 , 深度拷贝就要注意了 , 最好使用new关键字返回新的对象 。
publicBookgetBook(){Bookclone=newBook();clone.setPrice(this.book.getPrice());clone.setName(this.book.getName());returnclone;}关于不可变对象 , 请点击下面的链接查看我之前写了一篇文章 。
这次要说不明白immutable类 , 我就怎么地
最后希望这12个精致的字符串操作小技巧可以帮助大家巩固一波基础 , 反正我自己已经重新巩固了一波 , 很有收获的样子 , 感觉就像是“一群小精灵在我脑子里跳舞一样” , 学它就对了!
原文作者:沉默王二
原文链接:


    推荐阅读