我有一个返回String的方法。如果它返回空字符串或"<empty>"
,我想用默认值替换它,例如null
。假设它的名字是getSomeString
,这是一个昂贵的操作,所以我只能调用它一次,而且我不能将它的返回类型改为Option[String]
。现在,我正在做以下事情:
val myStr = {
val s = getSomeString
if (s == null || s.isEmpty) "<empty>" else s
}
有没有更简单的方法来实现同样的目标?
答案 0 :(得分:21)
val myStr = Option(getSomeString).filterNot(_.isEmpty).getOrElse("<empty>")
我发布此代码是因为我认为此代码中的意图比if / else或模式匹配版本更明确,但我没有考虑性能问题。
正如评论中提到的其他人一样,这个代码比简单的if / else或模式匹配慢得多(这一行会创建很多新对象,这是一项昂贵的操作),所以当性能是一个时,请不要使用此代码问题。
答案 1 :(得分:13)
鉴于功能昂贵:
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不仅能够快速回答问题,还能鼓励一些开箱即用的思维。
答案 2 :(得分:2)
您可以使用Option
在第一步中用空字符串替换null,然后在结果为空时替换为默认文本(无论是因为它原来是空的还是因为它为空):
Option(getSomeString).getOrElse("").replaceAll("^$","<empty>")
答案 3 :(得分:2)
您可以使用隐式值类
向String
添加方法
object ImplicitClassContainer {
implicit class RichString(val s: String) extends AnyVal {
def getOrDefault(defaultValue: String): String = {
s match {
case null | "" => defaultValue
case x => x
}
}
}
像这样使用
import ImplicitClassContainer._
println("hi".getOrDefault("<empty1>"))
println("".getOrDefault("<empty2>"))
val s: String = null
println(s.getOrDefault("<empty3>"))
所以即使null
上的方法调用也会得到优雅处理(Scala 2.10.1)。
答案 4 :(得分:1)
val myStr = getSomeString match { case ""|null => "<empty>" case s => s }
答案 5 :(得分:1)
如果您不需要更改:
Option(getSomeString).fold("<empty>")(s => s)
如果需要修改if为非空结果。简单的例子:
Option(getSomeString).fold("<empty>")(str => s"xxxxxxxx$str")