2013-08-24 61 views
11

我有一個返回字符串的方法。如果它返回空字符串或null,我想用默認值替換它,例如"<empty>"。我們假設它的名字是getSomeString,這是一個昂貴的操作,所以我只能調用它一次,我不能將其返回類型更改爲Option[String]。目前,我正在做以下工作:如何在Scala中用簡潔的字符串替換空字符串(或null)

val myStr = { 
    val s = getSomeString 
    if (s == null || s.isEmpty) "<empty>" else s 
} 

是否有更簡單的方法來實現相同的目的?

+0

如果不是用於isEmpty測試,您可以直接使用Option(getSomeString)將其轉換爲Option [String]。在練習中你會得到空字符串還是空字符串? –

+0

是的,不幸的。 – trustin

回答

12

鑑於昂貴的功能:

scala> def s(i: Int): String = i match { case 0=>null case 1=>"" case 2=>"hi" } 
s: (i: Int)String 

我覺得這是很容易閱讀和自由開銷,CF this in the wild

scala> def q(i: Int) = s(i) match { case ""|null => "<empty>" case x => x } 
q: (i: Int)String 

scala> q(0) 
res3: String = <empty> 

scala> q(1) 
res4: String = <empty> 

scala> q(2) 
res5: String = hi 

我的眼睛,這並不像表現,甚至與簡約標點符號:

scala> Option(s(0)) filterNot (_.isEmpty) getOrElse "<empty>" 
res6: String = <empty> 

此外,對比成本anonfun類的封鎖和附加的方法調用:

scala> :javap - 
    Size 1161 bytes 
    MD5 checksum 765f5f67b0c574252b059c8adfab1cf0 
    Compiled from "<console>" 
[...] 
     9: getstatic  #26     // Field scala/Option$.MODULE$:Lscala/Option$; 
     12: getstatic  #31     // Field .MODULE$:L; 
     15: iconst_0  
     16: invokevirtual #35     // Method .s:(I)Ljava/lang/String; 
     19: invokevirtual #39     // Method scala/Option$.apply:(Ljava/lang/Object;)Lscala/Option; 
     22: new   #41     // class $anonfun$1 
     25: dup   
     26: invokespecial #42     // Method $anonfun$1."<init>":()V 
     29: invokevirtual #48     // Method scala/Option.filterNot:(Lscala/Function1;)Lscala/Option; 
     32: new   #50     // class $anonfun$2 
     35: dup   
     36: invokespecial #51     // Method $anonfun$2."<init>":()V 
     39: invokevirtual #55     // Method scala/Option.getOrElse:(Lscala/Function0;)Ljava/lang/Object; 
     42: checkcast  #57     // class java/lang/String 
     45: putfield  #17     // Field res6:Ljava/lang/String; 

模式匹配通常只是一個if-else,更小和更快(即使考慮到它不優化s == ""s.isEmpty):

scala> :javap -r #q 
    public java.lang.String q(int); 
    flags: ACC_PUBLIC 
    Code: 
     stack=2, locals=5, args_size=2 
     0: getstatic  #19     // Field $line3/$read$$iw$$iw$.MODULE$:L$line3/$read$$iw$$iw$; 
     3: iload_1  
     4: invokevirtual #22     // Method $line3/$read$$iw$$iw$.s:(I)Ljava/lang/String; 
     7: astore_3  
     8: ldc   #24     // String 
     10: aload_3  
     11: invokevirtual #28     // Method java/lang/Object.equals:(Ljava/lang/Object;)Z 
     14: ifeq   22 
     17: iconst_1  
     18: istore_2  
     19: goto   33 
     22: aload_3  
     23: ifnonnull  31 
     26: iconst_1  
     27: istore_2  
     28: goto   33 
     31: iconst_0  
     32: istore_2  
     33: iload_2  
     34: ifeq   44 
     37: ldc   #30     // String <empty> 
     39: astore  4 
     41: goto   47 
     44: aload_3  
     45: astore  4 
     47: aload   4 
     49: areturn  

但是受其他答案的啓發,即使我永遠不會將此代碼帶回家與我的父母見面(因爲如果昂貴的函數返回該值,它會錯誤地將值"null"轉換成值 - 儘管這可能是一個功能)這裏是一個正則表達式:

scala> def p(i: Int) = "" + s(i) replaceAll ("^null$|^$", "<empty>") 
p: (i: Int)String 

"" + s(i)String.valueOf的簡寫,這當然產生字符串"null"爲空引用值。我非常感謝SO的能力,不僅能快速回答問題,還能鼓勵一些開箱即用的想法。

+0

javap很難閱讀你的文章並看到你的觀點。博士是什麼?如果其他模式匹配更快? – huynhjl

+0

@huynhjl Thx反饋。我編輯過,以免埋頭。 (報紙術語,而不是金屬。)不是我期望在SO上贏得心靈/思想。 –

+0

@huynhjl事實上,更強烈地說,這是一件不容易的事情。但如果孩子們覺得使用Option很有趣,那麼他們可能會做的更糟糕的事情,比如街頭暴動,造成真正的混亂。 –

19
val myStr = Option(getSomeString).filterNot(_.isEmpty).getOrElse("<empty>") 

更新

我張貼了這個代碼,因爲我想在這個代碼的意圖非常明顯比if/else語句或模式匹配的版本,但我並沒有考慮性能問題。

正如其他人在評論中提到的,這段代碼比簡單的if/else或模式匹配要慢很多(這一行會創建很多新對象,這是一個昂貴的操作),所以請不要在性能爲一個問題。

+1

與原始代碼相比,看起來*看起來不那麼簡單(此外,這段代碼會比前一段代碼慢得多)。 –

+1

我覺得它很乾淨。 –

+1

它不像我的答案那樣乾淨廉價。檢查碳足跡。 –

1

你可以使用Option替換空在第一步空字符串,然後用默認的文本替換,如果結果是空的(無論是因爲它是空的最初還是因爲它爲空):

Option(getSomeString).getOrElse("").replaceAll("^$","<empty>") 
2

你可以使用隱含值類

object ImplicitClassContainer { 
    implicit class RichString(val s: String) extends AnyVal { 
    def getOrDefault(defaultValue: String): String = { 
     s match { 
     case null | "" => defaultValue 
     case x => x 
     } 
    } 
    } 

添加一個方法來String使用這樣

import ImplicitClassContainer._ 

println("hi".getOrDefault("<empty1>")) 

println("".getOrDefault("<empty2>")) 

val s: String = null 
println(s.getOrDefault("<empty3>")) 

所以即使在null的方法調用正常處理(斯卡拉2.10 0.1)。

+0

http://docs.scala-lang.org/overviews/core/implicit-classes.html – axaluss

+0

@axaluss我不明白你的評論:答案使用[隱式*值*類](http://docs.scala -lang.org/sips/pending/value-classes.html):它'擴展AnyVal' – Beryllium

+1

我只是想添加文檔鏈接。 – axaluss

0
val myStr = getSomeString match { case ""|null => "<empty>" case s => s }