【Java】 如何优雅的做字符串的拼接
在 Java 中,我们都是如何的去做到一个字符串的拼接呢?字符串拼接是我们在Java代码中比较经常要做的事情,就是把多个字符串拼接到一起。都知道,String 是 Java 中一个不可变的类,所以一旦被实例化就无法被修改。那么我们怎么去优雅的拼接我们的字符串呢?老夫这里找了一堆的技术资料,基本上Java中的字符串的拼接都在这了。
上面有提到,字符串是不可变的,那么字符串的拼接又是怎么去拼接呢?其实这里讲的字符串的拼接就是将两个字符串拼成新的字符串,也就是最后一共三个字符串。
用 + 来拼接,多简单的事呀
这个应该算是最简单的一种方式了,但是很遗憾得玩告诉你,阿里巴巴在他们的规范里面之处不建议在 for 循环里面使用 “+” 进行字符串的拼接。这里的不建议,其实就是不允许的意思,只是人家说的比较委婉而已。事实上,现在还在拿 “+” 来做拼接的应该是比较少了吧。
我在逛阿里开发者社区的时候看到一篇文章《为什么阿里巴巴不建议在for循环中使用”+”进行字符串拼接》,里面提到这个 拼接符号 “+” 不是一个运算符重载,Java也并不支持这个所谓的运算符重载。作者提出这是 Java 的一个语法糖。
运算符重载:在计算机程序设计中,运算符重载(英语:operator overloading)是多态的一种。运算符重载,就是对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型。
语法糖:语法糖(Syntactic sugar),也译为糖衣语法,是由英国计算机科学家彼得·兰丁发明的一个术语,指计算机语言中添加的某种语法,这种语法对语言的功能没有影响,但是更方便程序员使用。语法糖让程序更加简洁,有更高的可读性。
concat
这是 String 里面提供的方法,用法如下:
String strA = "Hello" ; String strB = "world" ; String concat = strA.concat(",").concat(strB);
内部实现就是 将字符数组扩容后形成一个新的字符数组 buf , 再将参数 str 加进去。最后再将这个字符数组转成字符串。
public String concat(String str) { int otherLen = str.length(); if (otherLen == 0) { return this; } int len = value.length; char buf[] = Arrays.copyOf(value, len + otherLen); str.getChars(buf, len); return new String(buf, true); }
StringBuffer / StringBuilder
这两个应该可以说是一家的孪生兄弟了。做字符串拼接都是 append() 方法:
StringBuilder 里面的 append(String str) 的方法如下: 其实和 concat 方法差不多是吧。
@Override public StringBuilder append(String str) { super.append(str); return this; } // 这是 super.append() public AbstractStringBuilder append(String str) { if (str == null) return appendNull(); int len = str.length(); ensureCapacityInternal(count + len); str.getChars(0, len, value, count); count += len; return this; }
StringBuffer 里面的 append(String str)的方法如下:
@Override public synchronized StringBuffer append(String str) { toStringCache = null; super.append(str); return this; } public AbstractStringBuilder append(String str) { if (str == null) return appendNull(); int len = str.length(); ensureCapacityInternal(count + len); str.getChars(0, len, value, count); count += len; return this; }
我们可以看到,我们所实现的 append 方法是一样的,唯一的不同就是 StringBuffer 是一个线程安全的拼接。我们可以看一下这两个的类的继承关系。
StringUtils.join
这个方法不是Java的,而是Apache的一个方法。方法在 apache.commons 里面。
StringUtils.join(strA, ",", strA)
这个方法最主要的功能是: 将数组或集合以某拼接符拼接到一起形成新的字符串。
String []list ={"hello","world"}; StringUtils.join(list, ",")
在Java 8 里面也有一个 join 方法:
String message = String.join("-", "Java", "is", "cool"); List<String> strings = new LinkedList<>(); strings.add("Java"); strings.add("is"); strings.add("cool"); String message = String.join(" ", strings); //message returned is: "Java is cool" Set<String> strings = new LinkedHashSet<>(); strings.add("Java"); strings.add("is"); strings.add("very"); strings.add("cool"); String message = String.join("-", strings); //message returned is: "Java-is-very-cool"
Java8 中的新技能 StringJoiner
刚刚又说到 Java8 里面也有 join 方法,老夫查看第一个 join 方法,找到一个新的类。我们可以看下这个新的 类 StringJoiner.java
public static String join(CharSequence delimiter, CharSequence... elements) { Objects.requireNonNull(delimiter); Objects.requireNonNull(elements); // Number of elements not likely worth Arrays.stream overhead. StringJoiner joiner = new StringJoiner(delimiter); for (CharSequence cs: elements) { joiner.add(cs); } return joiner.toString(); }
当我们StringJoiner(CharSequence delimiter)初始化一个StringJoiner的时候,这个delimiter其实是分隔符,并不是可变字符串的初始值。
我们看到这个 joiner.add(cs) ,然后又做了 一个 joiner.toString() . 是不是很像我们的StringBuilder 里面的 append 方法 ? 我们进去看下这个方法。
public StringJoiner add(CharSequence newElement) { prepareBuilder().append(newElement); return this; }
现在是不是和 StringBuilder 很像?我们再来看这个 prepareBuilder() 是个啥玩意
private StringBuilder prepareBuilder() { if (value != null) { value.append(delimiter); } else { value = new StringBuilder().append(prefix); } return value; }
我们看到了,其实他就是一个StringBuilder. 那么他的性能就应当和我们的StringBuilder 不相上下。
在 Java 8 中 引入了这个 StringJoiner , 自然不是平白无故的引入。Java中,我们看到了许多新鲜元素,比如说 Stream .
举个例子:
现在有一个 集合 :
List<String> list = ImmutableList.of("Java" , "is" , "the" , "best" , "language");
现在,我需要的形式是这个:
Java is the best language
+
想要用 + , 那就要遍历,肯定是循环加 “+” , 那我们就直接 pass 了。
concat
String str = "" ; for (int i = 0; i < list.size(); i++) { str = str.concat(" ").concat(list.get(i)) ; }
StringBuilder
StringBuilder sb = new StringBuilder() ; for (int i = 0; i < list.size(); i++) { sb.append(list.get(i)).append(" ") ; }
也可以这么写:
String result = list.stream().reduce(new StringBuilder(), (sb, str) -> sb.append(str).append(" "), StringBuilder::append).get();
当然,这里要是不想用 StringBuilder , 就可以换成 + , 看起来也简单
String result = list.stream().reduce((a,b)->a+" "+b).get();
StringJoiner
以上的方法,其实很常见,也很简单,现在看看这个怎么用:
String result = list.stream().collect(Collectors.joining(" "));
就这样。这里的实现如下:
public static Collector<CharSequence, ?, String> joining(CharSequence delimiter) { return joining(delimiter, "", ""); } public static Collector<CharSequence, ?, String> joining(CharSequence delimiter, CharSequence prefix, CharSequence suffix) { return new CollectorImpl<>( () -> new StringJoiner(delimiter, prefix, suffix), StringJoiner::add, StringJoiner::merge, StringJoiner::toString, CH_NOID); }
总结
现在问题来了,以上的这么多方法都好用,怎么选?
- 不涉及循环的,就是那种很简单的那种拼接,就用 + ,简单方便 ;
- 涉及到循环的,比如说 for 的,可以考虑使用 StringBuilder , 要求线程安全的就选择 StringBuffer ;
- 有 List 这种的,StringJoiner 不免一个好的选择。
- concat 就看你心情了,不想用 + 就用他吧。
其实呢,在字符串额度拼接里面,老夫还用过这两个,应该是用的比较多的:
- String java = String.format("%s is the beat language", "Java");
- MessageFormat.format("{0} is the beat language" , "Java") ;
MessageFormat 方法在 java.text 里面,所以呢,他对一些 文本类数据(String)比较友好。曾经在使用这个方法的时候, 我有一个数字,20 000 000 的一个数,经过这个类转成了 20,,000,000 。 而用 String.format() 就没这个问题。具体的看大家选择。这两种方式其实并不能称之为字符串的拼接,顶多就是字符串的格式化。 format 方法不就是格式化额度意思吗。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
阿里云IoT Studio服务开发操作OSS存储服务示例
概述 IoT Studio服务开发是一个物联网业务逻辑的开发工具,通过编排服务节点的方式快速完成简单的物联网业务逻辑的设计。本文演示如何使用NodeJS节点基于OSS NodeJS SDK操作阿里云OSS存储服务。 Step By Step 1、拖拽服务开发控件 2、安装ali-oss 3、编写NodeJs脚本 /** * @param {Object} payload 上一节点的输出 * @param {Object} node 指定某个节点的输出 * @param {Object} query 服务流第一个节点的输出 * @param {Object} context { appKey, appSecret } */ module.exports = async function(payload, node, query, context) { // const result; let OSS = require('ali-oss'); // region https://help.aliyun.com/document_detail/31837.html?spm=a2c4g.11...
- 下一篇
C# 中的IComparable和IComparer
前言 在开发过程中经常会遇到比较排序的问题,比如说对集合数组的排序等情况,基本类型都提供了默认的比较算法,如string提供了按字母进行排序,而int整数则是根据整数大小进行排序.但是在引用类型中(具有多个字段),那么这个排序当然也是取决于我们特定的值。 IComparable接口 该接口由其值可以排序或排序的类型实现,并提供强类型的比较方法以对泛型集合对象的成员进行排序,例如数字可以大于第二个数字,一个字符串可以在另一个字符串之前以字母顺序出现。他要求实现类型定义的一个方法,CompareTo(T)该方法指示当前实现在排序顺序中的位置是在同一个类型和第二个对象之前、之后还是与其相同。通常,不会直接从开发人员代码中调用方法。相反他由List.Sort()和Add等方法自动调用。 通常,提供Icomparable实现的类型还IEquatable实现接口。IEquatable接口Equals定义方法,该方法确定实现类型的实例的相等性。 CompareTo(T)方法的实现必须Int32返回具有以下三个值之一的,如下表所示。 值 含义 小于零 此对象在排序顺序中位于CompareTo方法所指定...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
-
Docker使用Oracle官方镜像安装(12C,18C,19C)
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS8编译安装MySQL8.0.19
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS7,8上快速安装Gitea,搭建Git服务器
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
推荐阅读
最新文章
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- MySQL8.0.19开启GTID主从同步CentOS8
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2整合Redis,开启缓存,提高访问速度
- Windows10,CentOS7,CentOS8安装Nodejs环境
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- CentOS7设置SWAP分区,小内存服务器的救世主