字符串拼接的几种方式

+ 号拼接

        通过+拼接是最常见的拼接方式,这个应该算是最简单的一种方式了,但是很遗憾得玩告诉你,阿里巴巴在他们的规范里面之处不建议在 for 循环里面使用 “+” 进行字符串的拼接。这里的不建议,其实就是不允许的意思,只是人家说的比较委婉而已。事实上,现在还在拿 “+” 来做拼接的应该是比较少了吧。

        阿里开发者社区的时候看到一篇文章《为什么阿里巴巴不建议在for循环中使用”+”进行字符串拼接》,里面提到这个 拼接符号 “+” 不是一个运算符重载,Java也并不支持这个所谓的运算符重载。作者提出这是 Java 的一个语法糖。

运算符重载:在计算机程序设计中,运算符重载(英语:operator overloading)是多态的一种。运算符重载,就是对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型。

语法糖:语法糖(Syntactic sugar),也译为糖衣语法,是由英国计算机科学家彼得·兰丁发明的一个术语,指计算机语言中添加的某种语法,这种语法对语言的功能没有影响,但是更方便程序员使用。语法糖让程序更加简洁,有更高的可读性。

    

测试

public static void main(String[] args) {
    String str1 = "hello";
    String str2 = "world";
    String result = str1 + str2;
}

字节码文件

   L0
    LINENUMBER 12 L0
    LDC "hello"
    ASTORE 1
   L1
    LINENUMBER 13 L1
    LDC "world"
    ASTORE 2
   L2
    LINENUMBER 14 L2
    NEW java/lang/StringBuilder
    DUP
    INVOKESPECIAL java/lang/StringBuilder.<init> ()V
    ALOAD 1
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    ALOAD 2
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
    ASTORE 3

从字节码文件可以看出:String result = str1 + str2被JDK编译器优化成了

String result = new StringBuilder().append(str1).append(str2);

若将其拆分

 String s = "hello";
 s += "world";

查看字节码文件

   
 L5
     LINENUMBER 15 L5
     NEW java/lang/StringBuilder
     DUP
     INVOKESPECIAL java/lang/StringBuilder.<init> ()V
     ALOAD 5
     INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
     ALOAD 2
     INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
     INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
     ASTORE 5
    L6
     LINENUMBER 16 L6
     NEW java/lang/StringBuilder
     DUP
     INVOKESPECIAL java/lang/StringBuilder.<init> ()V
     ALOAD 5
     INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
     ALOAD 3
     INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
     INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
     ASTORE 5
    L7
     LINENUMBER 17 L7
     NEW java/lang/StringBuilder
     DUP
     INVOKESPECIAL java/lang/StringBuilder.<init> ()V
     ALOAD 5
     INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
     ALOAD 4
     INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
     INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
     ASTORE 5

可以看到,每次拼接都会创建一个StringBuilder

String.concat(String str)

concat方法是String给我们提供的拼接字符串的方法,内部实现就是 将字符数组扩容后形成一个新的字符数组 buf , 再将参数 str 加进去。最后再将这个字符数组转成字符串。

 public static void main(String[] args) {
     String str1 = "hello";
     String str2 = "world";
     String result = str1.concat(str2);
 }

源码描述如下:

将指定的字符串连接到该字符串的末尾。如果参数字符串的长度为0,则返回此String对象。否则,返回一个String对象,该对象表示一个字符序列,该字符序列是此String对象表示的字符序列与参数字符串表示的字符序列的串联。

源码

public String concat(String str) {
     int otherLen = str.length(); // 获取参数字符串长度
     if (otherLen == 0) {
         return this; // 参数长度为0,返回自身
     }
     int len = value.length; //获取自身长度
     char buf[] = Arrays.copyOf(value, len + otherLen); // 得到一个包含当前字符序列,长度为                                                  // 两者之和的字符数组
     str.getChars(buf, len); // 从当前字符序列长度开始,将参数的字符序列写入buf字符数组
     return new String(buf, true); // 创建新的String对象并返回。
 }

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 是一个线程安全的拼接

测试

      public static void main(String[] args) {
         String str1 = "hello";
         String str2 = "world";
         String result = new StringBuilder().append(str1).append(str2).toString();
     }

查看字节码文件

   L0
     LINENUMBER 13 L0
     LDC "hello"
     ASTORE 1
    L1
     LINENUMBER 14 L1
     LDC "world"
     ASTORE 2
    L2
     LINENUMBER 15 L2
     NEW java/lang/StringBuilder
     DUP
     INVOKESPECIAL java/lang/StringBuilder.<init> ()V
     ALOAD 1
     INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
     ALOAD 2
     INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
     INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
     ASTORE 3

可以看出,与 +号拼接一致。

String 字符串拼效率对比

先知道一点,String在Java中是不可变对象,因此每次拼接都是生成新的String对象,为了解决频繁的内存开辟消耗资源,才有了StringBuilder类。在+拼接过程中,JDK默认优化成为StringBuilder以提高运行效率。

但是这里又出现了一个问题,当且仅有两个字符串拼接生成一个新的字符串,这个默认优化的优势就体现不出来了。因为本来只需要三份String空间,默认优化StringBuilder的情况下,还需要一份StringBuilder的空间,多开辟了一份空间,肯定会对性能有所影响,

现在问题来了,以上的这么多方法都好用,怎么选?

  • 不涉及循环的,就是那种很简单的那种拼接,就用 + ,简单方便 ;

  • 非循环体中的字符串拼接,若只是两个字符串拼接,推荐使用concat

  • 涉及到循环的,比如说 for 的,可以考虑使用 StringBuilder , 要求线程安全的就选择 StringBuffer ;

  • 有 List 这种的,StringJoiner 不免一个好的选择。

### SystemVerilog 中的拼接符号用法 在 SystemVerilog 中,拼接符号 `{}` 被广泛应用于位级操作,主要用于将多个信号组合成一个更大的向量或将单个信号拆分为更小的部分。这种语法非常灵活,适用于多种场景。 #### 基本定义 拼接符号 `{}` 是一种强大的工具,允许开发者通过指定顺序来创建新的位矢量[^2]。它不仅可以用于简单的信号连接,还可以与其他常数或表达式一起使用。 #### 示例代码 以下是几个常见的使用案例: ```systemverilog // 将两个信号 a 和 b 进行拼接 logic [7:0] a; logic [7:0] b; logic [15:0] c; assign c = {a, b}; // 结果是一个 16 位宽的信号,其中高字节来自 a,低字节来自 b // 添加常数值进行拼接 logic [3:0] d; logic [7:0] e; assign e = {4'd0, d}; // 在 d 的高位填充 4 个零,形成一个新的 8 位信号 // 复制操作符的应用 (replication operator) logic [3:0] f; logic [11:0] g; assign g = {3{f}}; // 将 f 的值重复三次并拼接在一起,形成一个 12 位宽的信号 ``` #### 特殊情况说明 当涉及到复杂的硬件描述时,拼接符号可以用来实现特定的功能需求。例如,在某些情况下可能需要对输入信号重新排列或者扩展其宽度。 #### 动态数组与拼接 虽然动态数组本身并不直接涉及拼接符号,但在实际应用中两者经常结合使用。比如可以通过先构建一个动态数组再利用拼接将其转换为固定长度的寄存器形式[^4]。 ```systemverilog dynamic logic [7:0] array []; array = new[3]; array[0] = 8'hFF; array[1] = 8'hAA; array[2] = 8'h55; logic [23:0] result; assign result = {>>{array}}; // 使用流平展操作符 >>{} 将动态数组的内容按序展开为单一向量 ``` 以上展示了如何运用 `{}` 来完成不同类型的信号处理任务,并且强调了该特性对于 RTL 设计的重要性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值