I understand that these methods may be considered "equivalent" or "synonyms", albeit not "identical", according to questions such as Is the best Scala convention to invoke collection.size or collection.length and Scala Buffer: Size or Length?.
But does that mean they are the same?
Don't offer me opinions or wishy-washy notions of sameness. Just tell me when they're not actually the same.
Back up your claim with hard science!
Byte code will suffice. I'm not so interested in JIT-specific optimizations.
I'm especially interested in the current state of the world, namely, Scala 2.11 in recent milestones.
String and Array are still wrapped for size in 2.11.
You should train your fingers to type s.length
and arr.length
.
That's not hard, because both strings and arrays have length.
Someone coming to Scala from Java will often decide whether to use the methods on java.lang.String
or the convenient augmentations. Since we have a finite "decision budget" before we are required to replenish it with donuts from the break room, it's worth making a few of these decisions up front as a policy matter.
In case one has nothing better to discuss at lunch, or over donuts:
22: invokevirtual #41 // Method scala/Predef$.augmentString:(Ljava/lang/String;)Ljava/lang/String;
25: invokespecial #44 // Method scala/collection/immutable/StringOps."<init>":(Ljava/lang/String;)V
28: invokevirtual #47 // Method scala/collection/immutable/StringOps.size:()I
18: invokevirtual #39 // Method scala/Predef$.intArrayOps:([I)Lscala/collection/mutable/ArrayOps;
21: invokeinterface #44, 1 // InterfaceMethod scala/collection/mutable/ArrayOps.size:()I
Wait, wait, with -optimise you can lose the augmentString.
It's GenSeqLike
that introduces length
and specifies that it "yields the same result" as size
. SeqLike
implements it that way.
In fact, String
acquires a size
method by augmentation to a SeqLike
, specifically, StringLike
. That requires wrapping. In fact, it's not easy to predict which calls require wrapping, or dispatch to an extension method.
For instance, length
is defined in a way that wrapping is not required. That's because StringOps
is a value class. In fact, it compiles to String.length
.
However, the apply
method on StringOps
, defined as a call to charAt
to mimic an array apply, though defined in the same way, calls the extension method.
(A few opcodes and allocations may not mean much after JIT compilation, of course.)
scala> val s = "hello"
s: String = hello
scala> val n = s.length ; val i = s.size ; val c = s(4) ; s.slice(0,4)
n: Int = 5
i: Int = 5
c: Char = o
res0: String = hell
scala> :javap -prv -
public $line4.$read$$iw$$iw$();
flags: ACC_PUBLIC
Code:
stack=5, locals=7, args_size=1
0: aload_0
1: invokespecial #32 // Method java/lang/Object."<init>":()V
4: aload_0
5: putstatic #34 // Field MODULE$:L$line4/$read$$iw$$iw$;
8: aload_0
9: getstatic #39 // Field $line3/$read$$iw$$iw$.MODULE$:L$line3/$read$$iw$$iw$;
12: invokevirtual #42 // Method $line3/$read$$iw$$iw$.s:()Ljava/lang/String;
length
:
15: invokevirtual #47 // Method java/lang/String.length:()I
18: putfield #22 // Field n:I
21: aload_0
new StringOps
, or StringOops
:
22: new #49 // class scala/collection/immutable/StringOps
25: dup
26: getstatic #54 // Field scala/Predef$.MODULE$:Lscala/Predef$;
29: getstatic #39 // Field $line3/$read$$iw$$iw$.MODULE$:L$line3/$read$$iw$$iw$;
32: invokevirtual #42 // Method $line3/$read$$iw$$iw$.s:()Ljava/lang/String;
35: astore_2
36: astore_1
37: aload_2
StringOps.size
:
38: invokespecial #57 // Method scala/collection/immutable/StringOps."<init>":(Ljava/lang/String;)V
41: invokevirtual #60 // Method scala/collection/immutable/StringOps.size:()I
44: putfield #25 // Field i:I
47: aload_0
48: getstatic #65 // Field scala/collection/immutable/StringOps$.MODULE$:Lscala/collection/immutable/StringOps$;
51: getstatic #54 // Field scala/Predef$.MODULE$:Lscala/Predef$;
54: getstatic #39 // Field $line3/$read$$iw$$iw$.MODULE$:L$line3/$read$$iw$$iw$;
57: invokevirtual #42 // Method $line3/$read$$iw$$iw$.s:()Ljava/lang/String;
60: astore 4
62: astore_3
63: aload 4
65: iconst_4
apply
extension method:
66: invokevirtual #69 // Method scala/collection/immutable/StringOps$.apply$extension:(Ljava/lang/String;I)C
69: putfield #28 // Field c:C
72: aload_0
73: getstatic #65 // Field scala/collection/immutable/StringOps$.MODULE$:Lscala/collection/immutable/StringOps$;
76: getstatic #54 // Field scala/Predef$.MODULE$:Lscala/Predef$;
79: getstatic #39 // Field $line3/$read$$iw$$iw$.MODULE$:L$line3/$read$$iw$$iw$;
82: invokevirtual #42 // Method $line3/$read$$iw$$iw$.s:()Ljava/lang/String;
85: astore 6
87: astore 5
89: aload 6
91: iconst_0
92: iconst_4
and slice
:
93: invokevirtual #73 // Method scala/collection/immutable/StringOps$.slice$extension:(Ljava/lang/String;II)Ljava/lang/String;
96: putfield #31 // Field res0:Ljava/lang/String;
99: return