Strings in Dart

UPDATED: 2013-07-01

(This is part 7 in an ongoing series about Dart. Check out part 6, For loops in Dart.)

Dart brings some new ideas to Strings. If you think you know Strings, keep reading for a few surprises.

From the API docs: "A string is represented by a sequence of Unicode UTF-16 code units."

Immutable

String are immutable objects, which means you can create them but you can't change them. You can of course build a new string out of other strings, but once created, the string's contents are fixed.

This is an optimization, as two strings with the same characters in the same order can be the same object.

var s11 = "strings are immutable";
var s21 = "strings are immutable";

print(s11 == s21); // true, contain the same characters
print(identical(s11, s21)); // true, are the same object in memory

If you look closely at the String API docs, you'll notice that none of the methods on String actually change the state of a String. For example, the method replaceAll() returns a new String and doesn't exchange the original String.

var greetingTemplate = 'Hello NAME, nice to meet you!';
var greeting = greetingTemplate.replaceFirst(new RegExp("NAME"), 'Bob');

print(greetingTemplate != greeting); // true

Creating

Both single and double quotes can be used to declare a String.

print('single quotes work easily');

print("double quotes work just as well");

Multi-line strings are now supported! Just use a triple quote (''' or """), with either single quote or double quote. Note the initial newline is ignored, but the last newline is not ignored!

print( '''
the initial newline is ignored
but the last newline is not ignored
''');

// is the same as

print("""the initial newline is ignored
but the last newline is not ignored\n""");

Escaping

Some languages interpret single quotes as a "literal" string, but not Dart.

print('single quotes \n don\'t make a string literal');
> single quotes 
 don't make a string literal

You can make a "raw" string by prefixing it with r. For example:

print(r'prefixing with a r turns a string into a \n literal or "raw" string');
> prefixing with a r turns a string into a \n literal or "raw" string

Interpolation

Dart improves on other web programming languages by introducing string interpolation. Building and joining strings is much easier now:

var s = 'string interpolation';

print('dart has ${s} which is very handy');

print("you can nest ${s + ' for fun and ${"profit"}'} with Dart");

print("concatenating strings" + " " + "is also possible with the plus sign");
> dart has string interpolation which is very handy
> you can nest string interpolation for fun and profit with Dart
> concatenating strings is also possible with the plus sign, but we don't recommend it

Building from parts

Programmatically generating a String is best accomplished with a StringBuffer. A StringBuffer doesn't generate a new String object until toString() is called.

var sb = new StringBuffer();

sb.write("Use a StringBuffer");
sb.writeAll(["for ", "efficient ", "string ", "creation "]);
sb.write("if you are ")
sb.write("building lots of strings");

// or you can use method cascades:

sb
  ..write("Use a StringBuffer")
  ..writeAll(["for ", "efficient ", "string ", "creation "])
  ..write("if you are ")
  ..write("building lots of strings");

var fullString = sb.toString();

print(fullString); // Use a StringBufferfor efficient string creation if you are building lots of strings

sb.clear(); // all gone!

You might be thinking, "Pffft, typing StringBuffer is so many characters. The performance difference can't be that big of a deal!" to which I reply, "The numbers don't lie."

// use new hotness: StringBuffer
buildWithStringBuffer() {
  var sb = new StringBuffer();

  for (var i = 9999; i > 0; i--) {
    sb.add(i.toString());
    sb.add(" beers on the wall\n");
  }

  var finalString = sb.toString();
}

// use old school + for string concatenation
buildWithConcat() {
  var s = '';

  for (var i = 9999; i > 0; i--) {
    s += i.toString();
    s += " beers on the wall\n";
  }

  var finalString = s;
}

// wrap a function fn with the Stopwatch class to time execution
measure(fn) {
  var sw = new Stopwatch.start();
  fn();
  return sw.elapsedInMs();
}

// the following timings are from the Dart VM
// smaller is faster is better

measure(buildWithStringBuffer); // 20

measure(buildWithConcat); // 3574 !!

As you can see, using old school + concatenation for Strings is much slower than using StringBuffer.

Note: the above times were measured in the Dart VM. When compiled to JavaScript, you will see different numbers.

Matching

Thanks to the core Dart libraries, it's fairly easy to determine if a strings starts with, ends with, or contains another string.

var fullName = 'Cuthbert Musgrave Girdlestone, III';


fullName.startsWith('Cuthbert');            // true

fullName.endsWith('III');                   // true

fullName.contains('Musgrave');  // true

The contains() method can also take a RegExp object for the pattern.

Summary

Strings are immutable lists of Unicode UTF-16 code units. They can be multi-line with triple quotes, or "raw" and unfiltered with a @ prefix.

Strings can be interpolated, which is preferable to concatenation via the + operator. If you must programmatically build up a String, do so with StringBuffer, which is much faster.

Strings can do a lot more than we showed here. For instance, you can split a string, create a substringtrim it, and lots more.

Next Steps

Like what you see? Learn more about Dart, its HTML libraries, and join the discussion. File a new issue if you see something missing.

Also, follow @dart_lang and +Dartisans for more info!

Read Part 8, This is this in Dart, in which we no longer have to assign this to _this for closures.

Popular posts from this blog

Lists and arrays in Dart

Converting Array to List in Scala

Null-aware operators in Dart