string 如何在 Scala 中简洁地用默认字符串替换空字符串(或 null)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18417321/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 02:04:01  来源:igfitidea点击:

How to substitute an empty string (or null) with a default string concisely in Scala

stringscala

提问by trustin

I have a method that returns a String. I want to substitute it with a default value such as "<empty>"if it returns an empty string or null. Let's assume its name is getSomeString, it is an expensive operation so I can call it only once, and I can't change its return type to Option[String]. For now, I'm doing the following:

我有一个返回字符串的方法。我想用默认值替换它,例如"<empty>"如果它返回一个空字符串或null. 让我们假设它的名字是getSomeString,这是一个昂贵的操作,所以我只能调用它一次,而且我不能将它的返回类型更改为Option[String]。现在,我正在执行以下操作:

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

Is there a simpler way to achieve the same thing?

有没有更简单的方法来实现同样的目标?

采纳答案by som-snytt

Given an expensive function:

给定一个昂贵的函数:

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

I think this is easy to read and free of overhead, cf this in the wild:

我认为这是容易阅读和自由开销,CF在野外此

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

To my eyes, this is not as expressive, even with minimalist punctuation:

在我看来,即使使用极简标点符号,这也没有那么富有表现力:

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

Moreover, contrast the cost in anonfunclasses for the closures and additional method invocations:

此外,对比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
        25: dup           
        26: invokespecial #42                 // Method $anonfun."<init>":()V
        29: invokevirtual #48                 // Method scala/Option.filterNot:(Lscala/Function1;)Lscala/Option;
        32: new           #50                 // class $anonfun
        35: dup           
        36: invokespecial #51                 // Method $anonfun."<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;

The pattern match is generally just an if-else, smaller and faster (even considering that it doesn't optimise s == ""to s.isEmpty):

模式匹配通常只是一个 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       

But inspired by the other answer, even if I would never take this code home to meet my parents (because it incorrectly converts the value "null"if the expensive function returns it -- though maybe it's a feature to do that), here is a regex:

但是受到另一个答案的启发,即使我永远不会把这段代码带回家见我的父母(因为"null"如果昂贵的函数返回它,它会错误地转换该值——尽管它可能是这样做的一个功能),这里是一个正则表达式:

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

The "" + s(i)is a shorthand for String.valueOf, which of course produces the String "null"for a null reference value. I appreciate SO's ability not only to generate quick answers to questions, but to encourage some out-of-the-box thinking.

The"" + s(i)是 的简写String.valueOf,它当然会"null"为空引用值生成 String 。我很欣赏 SO 不仅能够快速回答问题,而且还能鼓励一些开箱即用的思维。

回答by Brian Hsu

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

Updated

更新

I posted this code because I think the intent in this code is clear than if/else or pattern matching version, but I didn't consider the performance issue.

我发布此代码是因为我认为此代码的意图比 if/else 或模式匹配版本更清楚,但我没有考虑性能问题。

As the others in comments mentioned, this code is much slower than simple if / else or pattern matching(this line will create a lot new objects which is an expensive operation), so please do not use this code when performance is an issue.

正如评论中的其他人所提到的,此代码比简单的 if / else 或模式匹配慢得多(此行将创建很多新对象,这是一项昂贵的操作),因此请不要在性能有问题时使用此代码。

回答by Beryllium

You could add a method to Stringusing an implicit value class

您可以添加一个方法来String使用隐式值类

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

to be used like this

像这样使用

import ImplicitClassContainer._

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

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

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

so even the method call on nullis handled gracefully (Scala 2.10.1).

所以即使是方法调用也null被优雅地处理(Scala 2.10.1)。

回答by Micha? Kosmulski

You could replace null with empty string in first step using Optionand then substitute with default text if result is empty (whether because it was empty originally or because it was null):

您可以在第一步中使用空字符串替换 null ,Option然后如果结果为空(无论是因为它最初为空还是因为它为空),则用默认文本替换:

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

回答by Roman Kazanovskyi

If you don't need changes:

如果您不需要更改:

Option(getSomeString).fold("<empty>")(s => s)

If you need to modify if for nonEmpty result. Simple example:

如果需要修改 if for nonEmpty 结果。简单的例子:

Option(getSomeString).fold("<empty>")(str => s"xxxxxxxx$str")

回答by dev-null

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