Scala: Строка "+" против "++"

Я новичок в Scala, и я видел код для конкатенации строк в Scala следующим образом:

"test " ++ "1"

И я проверил, и он также написан в Scala Doc

"test " + "1"

Итак, я понимаю, что + подобен Java String +, но ++ более мощный, может принимать больше типов параметров. Также ++ кажется универсальным для других вещей, таких как List. Я хочу знать, правильно ли я понимаю. и любые другие различия? Когда нужно один над другим только для конкатенации строк?

Ответ 1

Это помогает взглянуть на scala.Predef, чтобы увидеть, что именно происходит.

Если вы проверите там, вы увидите, что String в Scala - это просто псевдоним для java.lang.String. Другими словами, метод + в String переводится в оператор Java +.

Итак, если Scala String - это просто Java String, как вообще существует метод ++, спросите вы. (Ну, я бы спросил, по крайней мере.) Ответ заключается в том, что существует неявное преобразование из String в WrappedString, обеспечиваемое методом wrapString, который также находится в Predef.

Обратите внимание, что ++ берет любой экземпляр GenTraversableOnce и добавляет все элементы этого экземпляра к исходному WrappedString. (Обратите внимание, что в документах неверно указано, что метод возвращает WrappedString[B]. Это должно быть неверно, поскольку WrappedString не принимает параметры типа.) Что вы получите, так это либо String (если вы add - это Seq[Char]) или какой-то IndexedSeq[Any] (если это не так).

Вот несколько примеров:

Если вы добавите String к List[Char], вы получите строку.

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

Если вы добавите String к List[String], вы получите IndexedSeq[Any]. На самом деле, первые два элемента - это Char, но последние три - это String, как показывает следующий вызов.

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

Как отметил Алексей, ни один из них не является очень тонким инструментом, поэтому вам, вероятно, лучше использовать строковую интерполяцию или StringBuilder, если только для этого нет веской причины не,

Ответ 2

String - это TraversableLike, что означает, что его можно разложить на последовательность элементов (символов). Вот откуда приходит ++, иначе вы не можете сделать ++ в String. ++ будет работать только тогда, когда его правая сторона (или параметр этой функции) является декомпозиционным типом (или проходящим).

Теперь как String станет TraversableLike? Это означает, что вступают в действие те, которые определены в Predef. Один из неявных преобразует нормальный String в WrappedString, где WrappedString.canBuildFrom имеет весь клей, который в основном работает таким образом:

WrappedString.canBuildFromStringBuilderStringLikeIndexedSeqOptimizedIndexedSeqLikeSeqLikeIterableLikeTraversableLike

Так как implitsits, определенные в Predef, уже находятся в области видимости, можно написать такой код:

"test " ++ "1"

Теперь ваши вопросы:

Я хочу знать, правильно ли я понимаю. и любые другие различия?

Да, ваше понимание в правильном направлении.

Когда нужно один над другим только для конкатенации строк?

Для конкатенации строк ясно, что "test " + "1" создает меньше объектов и меньше числа вызовов функций. Тем не менее, я всегда предпочитал бы интерполяцию строк так:

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

что более читаемо.

Подробнее:

Ответ 3

Итак, я понимаю, что + похож на Java String +, но ++ более мощный, может принимать больше типов параметров

Дело в том, что + на Strings является более мощным в этом смысле: он может принимать любые параметры вообще, как и в Java. Это часто считается недостоверным (особенно потому, что он также работает со строками справа), но мы в значительной степени застряли в нем. ++ - это, как вы говорите, общий метод сбора и более безопасный тип ("test " ++ 1 не будет компилироваться).

Когда нужно один над другим только для конкатенации строк?

Я бы предпочел +. Тем не менее, для многих (я бы даже сказал, большинство), что вы не хотите, не используйте: строчную интерполяцию.

val n = 1
s"test $n"

И, конечно, при создании строки из многих частей используйте StringBuilder.

Ответ 4

++ не обязательно "более мощный", но он используется в целом как операция конкатенации/добавления. Однако он не выполняет назначение. IE listX ++ y будет добавляться в listX, но i++ не будет увеличивать целое число я (поскольку это назначает переменной в сравнении с мутированием).

Это, по крайней мере, мое понимание. Я не эксперт Scala.

Ответ 5

Существует неявное преобразование от 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]