Junchao Gu
Junchao Gu

Reputation: 1865

Scala: String "+" vs "++"

I am new to Scala and I have seen code for concatenating Strings in Scala like this:

"test " ++ "1"

And I have tested, and it is also written in Scala Doc

"test " + "1"

So my understanding is that + is like the Java String + but ++ is more powerful, can take in more types of parameters. Also ++ seems to be universal to other things like List. I want to know if my understanding is correct. and any other differences? When should one over another just for string concatenation?

Upvotes: 15

Views: 16374

Answers (5)

Todd O'Bryan
Todd O'Bryan

Reputation: 2260

It helps to take a look in scala.Predef to see what exactly is going on.

If you check there, you see that String in Scala is just an alias for java.lang.String. In other words, the + method on a String gets translated into Java's + operator.

So, if a Scala String is just a Java String, how does the ++ method even exist, you might ask. (Well, I'd ask, at least.) The answer is that there's an implicit conversion from String to WrappedString provided by the wrapString method, which is also in Predef.

Notice that ++ takes any GenTraversableOnce instance and adds all of the elements in that instance to the original WrappedString. (Note that the docs incorrectly state that the method returns a WrappedString[B]. This has to be incorrect, because WrappedString doesn't take type parameters.) What you'll get back is either a String (if the thing you add is a Seq[Char]) or some IndexedSeq[Any] (if it's not).

Here are some examples:

If you add a String to a List[Char], you get a String.

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

If you add a String to a List[String], you get an IndexedSeq[Any]. In fact, the first two elements are Chars, but the last three are Strings, as the follow-up call shows.

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)

Finally, if you add a String to a String with ++, you get back a String. The reason for this is that WrappedString inherits from IndexedSeq[Char], so this is a convoluted way of adding a Seq[Char] to a Seq[Char], which gives you back a Seq[Char].

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

As Alexey noted, neither of these is a very subtle tool, so you're probably better off using string interpolation or a StringBuilder unless there's a good reason not to.

Upvotes: 23

tuxdna
tuxdna

Reputation: 8487

String is a TraversableLike, which means it can be decomposed into a sequence of elements ( characters ). That is where ++ comes from, otherwise you can not do ++ on String. ++ would only work when the right hand side of it ( or the parameter to that function ), is a de-composable type ( or a traversable ).

Now how does String become a TraversableLike? This where the implicits defined in Predef come into play. One of the implicit converts normal String into a WrappedString where WrappedString.canBuildFrom has all the glue which basically works in this way:

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

Since the implicits defined in Predef are already in scope, it is possible to write code like this:

"test " ++ "1"

Now your questions:

I want to know if my understanding is correct. and any other differences?

Yes your understanding is in the right direction.

When should one over another just for string concatenation?

For string concatenation, clearly "test " + "1" is creating less objects, and less number of function calls. However, I would always prefer string interpolation like so:

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

which is more readable.

For more details:

Upvotes: 5

Jason Scott Lenderman
Jason Scott Lenderman

Reputation: 1918

There is an implicit conversion from String to StringOps in scala.Predef. The ++ method is defined in the StringOps class. So whenever you do str1 ++ str2 the scala compiler is essentially (at from the perspective of the coder) wrapping str1 in a StringOps and calling the ++ method of StringOps. Note that StringOps is essentially a sort of IndexedSeq, so the ++ operator is very flexible, e.g.

"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]

Upvotes: 0

Ironcache
Ironcache

Reputation: 1759

++ isn't necessarily "more powerful", but it is used in general as a concatenation/appending operation. However, it does not perform assignment. IE listX ++ y will append to listX, but i++ will not increment integer i (since that's assigning to the variable versus mutating).

That's my understanding at least. I'm not a Scala expert.

Upvotes: 0

Alexey Romanov
Alexey Romanov

Reputation: 170745

So my understanding is that + is like the Java String + but ++ is more powerful, can take in more types of parameters

The thing is, + on Strings is more powerful in this sense: it can take any parameters at all, just like in Java. This is often considered a misfeature (especially since it also works with strings on the right), but we are pretty much stuck with it. ++ is, as you say, a general collection method and more type-safe ("test " ++ 1 won't compile).

When should one over another just for string concatenation?

I'd prefer +. However, for many (I'd even say most) usages what you want is neither: use string interpolation instead.

val n = 1
s"test $n"

And of course, when building up a string from many parts, use StringBuilder.

Upvotes: 2

Related Questions