Do size and length in Scala collections generate t

2020-06-17 07:11发布

问题:

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.

回答1:

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   


标签: scala