问题 Scala:字符串“+”vs“++”


我是Scala的新手,我已经看到了在Scala中连接字符串的代码,如下所示:

"test " ++ "1"

我已经测试了,它也写了 Scala Doc

"test " + "1"

所以我的理解是这样的 + 就像Java String + 但 ++ 功能更强大,可以采用更多类型的参数。也 ++ 似乎是List等其他东西的普遍性。我想知道我的理解是否正确。和其他任何差异?什么时候应该另一个只是为了字符串连接?


4999
2017-10-12 05:42


起源

除了答案,我建议不要使用 + 不再。它很可能会被弃用并最终被删除。现在Scala有字符串插值,这是组成字符串的首选方法。运用 ++ 可能会更慢,但我认为这是一个很好的选择,语法清晰,与Scala的集合保持一致。 - 0__
@ 0__ 2小时前,+是来自Java字符串吗?我的意思是在那种情况下Scala会保留Scala字符串吗? - Junchao Gu
+ 没有定义 java.lang.String API,在Java中它只是运行字符串生成器的语法糖。在Scala中也是如此,其中String catenation with + 即使在没有的情况下,编译器也会定义 Predef.any2stringadd。也可以看看: stackoverflow.com/questions/2664274/... - 0__


答案:


它有助于了解一下 scala.Predef 看看究竟发生了什么。

如果你在那里检查,你会看到 String 在Scala只是一个别名 java.lang.String。换句话说, + 方法 String 被翻译成Java的 + 运营商。

所以,如果是Scala String 只是一个Java String,怎么样 ++ 方法甚至存在,你可能会问。 (好吧,我至少会问。)答案是有一个隐含的转换 String 至 WrappedString 由...提供 wrapString 方法,也在 Predef

请注意 ++ 接受任何 GenTraversableOnce 实例并将该实例中的所有元素添加到原始元素中 WrappedString。 (请注意,文档错误地指出该方法返回一个 WrappedString[B]。这必须是不正确的,因为 WrappedString 不接受类型参数。)你会得到的是a String (如果你添加的东西是 Seq[Char])或一些 IndexedSeq[Any] (如果不是)。

这里有些例子:

如果你添加一个 String 到了 List[Char],你得到一个字符串。

scala> "a" ++ List('b', 'c', 'd')
res0: String = abcd

如果你添加一个 String 到了 List[String]你得到一个 IndexedSeq[Any]。实际上,前两个元素是 Chars,但最后三个是 Strings,如后续电话所示。

scala> "ab" ++ List("c", "d", "e")
res0: scala.collection.immutable.IndexedSeq[Any] = Vector(a, b, c, d, e)

scala> res0 map ((x: Any) => x.getClass.getSimpleName)
res1: scala.collection.immutable.IndexedSeq[String] = Vector(Character, Character, String, String, String)

最后,如果你添加一个 String 到了 String 同 ++,你回来了 String。原因是这样的 WrappedString 继承自 IndexedSeq[Char],所以这是一种令人费解的添加方式 Seq[Char] 到了 Seq[Char],它给你一个 Seq[Char]

scala> "abc" + "def"
res0: String = abcdef

正如Alexey所说,这些都不是一个非常微妙的工具,所以你最好不要使用它 字符串插值 或者a StringBuilder 除非有充分理由不这样做。


11
2017-10-12 07:02





String 是一个 TraversableLike,这意味着它可以分解为一系列元素(字符)。那就是 ++ 来自,否则你做不到 ++ 在字符串上。 ++ 仅当它的右侧(或该函数的参数)是可解组合类型(或可遍历)时才会起作用。

现在怎么样 String 成为一个 TraversableLike?这就是定义中的含义 Predef 参加进来。其中一个隐式转换正常 String 变成一个 WrappedString 哪里 WrappedString.canBuildFrom 拥有基本上以这种方式工作的所有胶水:

WrappedString.canBuildFrom  - > StringBuilder  - > StringLike  - > IndexedSeqOptimized  - > IndexedSeqLike  - > SeqLike  - > IterableLike  - > TraversableLike

由于Predef中定义的implicits已经在范围内,因此可以编写如下代码:

"test " ++ "1"

现在你的问题:

我想知道我的理解是否正确。和其他任何差异?

是的,你的理解是正确的方向。

什么时候应该另一个只是为了字符串连接?

对于字符串连接,很明显 "test " + "1" 创建更少的对象,减少函数调用次数。但是,我总是喜欢字符串插值,如下所示:

val t1 = "test"
val t2 = "1"
val t3 = s"$t1 $t2"

哪个更具可读性。

更多细节:


3
2017-10-12 07:03





所以我的理解是+就像Java String +但是++更强大,可以接受更多类型的参数

事情是, + 在这个意义上,字符串更强大:它可以接受任何参数,就像在Java中一样。这通常被认为是错误的(特别是因为它也适用于右边的字符串),但我们几乎坚持它。 ++ 正如你所说,是一般的收集方法,更安全("test " ++ 1 不会编译)。

什么时候应该另一个只是为了字符串连接?

我更喜欢 +。然而,对于许多人(我甚至说最多)使用你想要的东西既不是:使用 字符串插值 代替。

val n = 1
s"test $n"

当然,从中构建一个字符串 许多 零件,使用 StringBuilder


2
2017-10-12 06:26



你是对的,“+”更强大,但我只想确认在Java中发生从int到string的隐式转换吗? - Junchao Gu
严格来说,没有隐含的转换。在Java中,所谓的是所谓的 字符串转换;在斯卡拉 String 被认为有 def +(other: Any)方法,所以它可以采取任何论点。隐式转换仅在非String 没有合适的价值 + 方法在左边和 String 在右边(见 any2stringAdd),在这种情况下,它发生在Scala中。 - Alexey Romanov


++不一定“更强大”,但它通常用作连接/追加操作。但是,它不执行分配。 IE listX ++ y 将附加到listX,但是 i++ 不会增加整数i(因为它分配给变量而不是变异)。

这至少是我的理解。我不是斯卡拉专家。


0
2017-10-12 06:32





有一个隐含的转换 String 至 StringOps 在 scala.Predef。该 ++ 方法定义于 StringOps 类。所以每当你做的时候 str1 ++ str2 scala编译器本质上(从编码器的角度来看)包装 str1 在一个 StringOps 并呼吁 ++ 的方法 StringOps。注意 StringOps 基本上是一种 IndexedSeq, 所以 ++ 操作员非常灵活,例如

"Hello, " ++ "world!"  //results in "Hello, world" with type String
"three" ++ (1 to 3)    //results in Vector('t', 'h', 'r', 'e', 'e', 1, 2, 3) with type IndexedSeq[AnyVal]

0
2017-10-12 07:02