【问题标题】:Scala: String "+" vs "++"Scala:字符串“+”与“++”
【发布时间】:2016-01-09 12:16:48
【问题描述】:

我是 Scala 的新手,我见过这样的 Scala 中连接字符串的代码:

"test " ++ "1"

而且我已经测试过了,也是写在Scala Doc

"test " + "1"

所以我的理解是+ 就像Java String +++ 更强大,可以接受更多类型的参数。 ++ 似乎也适用于 List 等其他事物。我想知道我的理解是否正确。和任何其他差异?什么时候应该为了字符串连接而一个接一个?

【问题讨论】:

  • 除了答案,我建议不要再使用+。它很可能会被弃用并最终被删除。现在 Scala 有了字符串插值,这是组成字符串的首选方式。使用++ 可能会慢一些,但我认为这是一个很好的替代方案,语法清晰并且与 Scala 的集合保持一致。
  • @0__ 2 小时前,+ 来自 Java 字符串,对吗?我的意思是在那种情况下,Scala 会为 Scala 字符串保留它吗?
  • + 没有在 java.lang.String API 上定义,在 Java 中它只是运行字符串生成器的语法糖。这在 Scala 中也是一样的,即使没有Predef.any2stringadd,编译器也会定义带有+ 的字符串连接。另见:stackoverflow.com/questions/2664274/…

标签: java string scala operators


【解决方案1】:

查看scala.Predef 以了解具体情况会有所帮助。

如果您在那里查看,您会发现 Scala 中的 String 只是 java.lang.String 的别名。换句话说,String 上的 + 方法被转换为 Java 的 + 运算符。

所以,如果 Scala String 只是一个 Java String,那么 ++ 方法是如何存在的,你可能会问。 (好吧,至少我会问。)答案是wrapString 方法提供了从StringWrappedString 的隐式转换,该方法也在Predef 中。

注意++ 采用任何GenTraversableOnce 实例并将该实例中的所有元素添加到原始WrappedString。 (请注意,文档错误地声明该方法返回WrappedString[B]。这一定是不正确的,因为WrappedString 不接受类型参数。)你会得到一个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 所指出的,这些都不是一个非常微妙的工具,因此您最好使用 string interpolationStringBuilder,除非有充分的理由不这样做。

【讨论】:

    【解决方案2】:

    String 是一个TraversableLike,这意味着它可以分解为一系列元素(字符)。这就是++ 的来源,否则你不能在String 上做++++ 仅在其右侧(或该函数的参数)是可分解类型(或可遍历)时才有效。

    现在String 如何变成TraversableLike?这就是Predef 中定义的隐式发挥作用的地方。其中一个隐式将普通String 转换为WrappedString,其中WrappedString.canBuildFrom 具有基本上以这种方式工作的所有胶水:

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

    由于 Predef 中定义的隐式已经在作用域内,因此可以编写如下代码:

    "test " ++ "1"
    

    现在你的问题:

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

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

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

    对于字符串连接,显然"test " + "1" 正在创建更少的对象和更少的函数调用。但是,我总是更喜欢这样的字符串插值:

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

    这更易读。

    更多详情:

    【讨论】:

      【解决方案3】:

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

      问题是,Strings 上的+ 在这个意义上更强大:它可以接受任何参数,就像在 Java 中一样。这通常被认为是一个错误功能(特别是因为它也适用于右侧的字符串),但我们几乎坚持下去。正如您所说,++ 是一种通用的收集方法,并且类型更安全("test " ++ 1 不会编译)。

      什么时候应该一个接一个才用于字符串连接?

      我更喜欢+。但是,对于许多(我什至会说大多数)用法,您想要的都不是:改用 string interpolation

      val n = 1
      s"test $n"
      

      当然,当从许多部分构建字符串时,请使用StringBuilder

      【讨论】:

      • 你说得对,“+”更强大,但我只是想确认从 int 到 string 的隐式转换发生在 Java 中,对吗?
      • 严格来说,不涉及隐式转换。在 Java 中,会发生所谓的string conversion;在 Scala 中 String 被认为具有 def +(other: Any) 方法,因此它可以接受任何参数。隐式转换只有在没有合适的+ 方法的非String 值在左侧和String 在右侧时才会发生(参见any2stringAdd),在这种情况下它发生在Scala 中。
      【解决方案4】:

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

      至少这是我的理解。我不是 Scala 专家。

      【讨论】:

        【解决方案5】:

        scala.Predef 中存在从StringStringOps 的隐式转换。 ++ 方法在 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]
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2017-11-07
          • 1970-01-01
          • 2012-01-11
          • 2011-12-03
          • 2011-09-08
          • 2016-03-17
          • 1970-01-01
          相关资源
          最近更新 更多