Scala 大小写匹配优化以提高性能和可读性
Scala case-match optimization for performance and readability
我在 Scala 中写了下面的模式匹配:
scala> val rowMap = Map("abc" -> null)
rowMap: scala.collection.immutable.Map[String,Null] = Map(abc -> null)
scala> val postgresKey = "abc"
postgresKey: String = abc
scala> val h2Key = "xyz"
h2Key: String = xyz
建议选项:
scala> (rowMap.get(postgresKey).map(_.asInstanceOf[String]) , rowMap.get(h2Key).map(_.asInstanceOf[String])) match
| {
| case (Some(value), _) if (value != null && value.trim.nonEmpty) => value
| case (None, Some(value)) if (value != null && value.trim.nonEmpty) => value
| case (_ , _) => "0.0"
| }
res9: String = 0.0
我的代码:
scala> (rowMap.get(postgresKey).map(_.asInstanceOf[String]) , rowMap.get(h2Key).map(_.asInstanceOf[String])) match
| {
| case (Some(value), _) if (value != null ) => if (value.trim.nonEmpty) value else "0.0"
| case (None, Some(value)) if (value != null ) => if (value.trim.nonEmpty) value else "0.0"
| case (_ , _) => "0.0"
| }
res10: String = 0.0
建议的选项是否比我的代码有任何性能优势。
我知道 &&
会在看到 null
时停止比较 value.trim.nonEmpty
。因此,我们节省了 1 次比较。
我明白阅读起来也更干净。
建议的方法还有什么更好的吗?
编辑 1:我被告知要避免使用 null
。然而,它是来自 anorm defaultParser
的输入。由于场景难以复制,我举了上面的例子。在我的例子中,单元测试在 H2 中,实际数据库是 postgres。
下面是代码片段:
val anormQuery = SQL(query)
// map the anorm Row as per the input params, differentiate into aggregated and group cols
val tmp = anormQuery.as(anormQuery.defaultParser.*)
logger.info(" Printing the result set object " + tmp.toString())
val finalSqlResultset = tmp.map(row ⇒ {
// Anorm row.asMap has this behaviour that it adds either a leading dot(.) or <tablename>. in front of the map keys (the columns/alias in sql) based on whether it is H2 or Postgres
val rowMap = for ((k, v) ← row.asMap) yield (k, v)
logger.info("Print the resultSet as map : " + rowMap.toString())
val aggregates = expressionsToAggregate.map(input ⇒ {
val (anormKey, postgresKey) = doesColumnHaveAlias.get(input.alias.getOrElse("UnknownAlias")).getOrElse(("Unknown", "Unknown"))
//
val aggResult = AggregatedValue(
input.alias.get,
(rowMap.get(postgresKey).map(_.asInstanceOf[String]), rowMap.get(anormKey).map(_.asInstanceOf[String])) match {
case (Some(value), _) if (value != null && value.trim.nonEmpty) ⇒ value
case (None, Some(value)) if (value != null && value.trim.nonEmpty) ⇒ value
case (_, _) ⇒ "0.0"
})
logger.info("## TRACE 1 ##" + aggResult)
aggResult
})
})
首先,不要使用空值。曾经。
就做 val rowMap = Map("abc" -> "")
.
或者更好的是,根本不要将垃圾放入地图(无论如何你都在过滤掉它!)。
其次,既然你提到了可读性,看起来你正在寻找这样的东西:
rowMap.get(postgresKey).filterNot(_.trim.isEmpty)
.orElse(rowMap.get(h2Key).filerNot(_.trim.isEmpty))
.getOrElse("0.0")
最后,回答您的问题,您显示的两个版本并不相同。
第一个做我上面做的。第二个是这样的:
rowMap.get(postgresKey).orElse(rowMap.get(h2Key))
.filterNot(_.trim.isEmpty)
.getOrElse("0.0")
在前一种情况下,将 postgresKey
设置为 ""
相当于根本不设置它,在这种情况下,您将始终获得 h2Key
的值。
在后一种情况下,如果 postgresKey
设置为 ""
,无论 h2Key
设置的是什么,您都将得到“0.0”。
所以,行为是不同的,这取决于你实际需要哪一个。
如果第一种行为是你需要的,也可以这样写:
Some(rowMap.filterValues(_.trim.nonEmpty)).flatMap { case m =>
m.get(postrgesKey) orElse m.get(h2Key)
}.getOrElse("0.0")
或者,如果您听了我的建议并决定不首先将垃圾放入地图中,您也可以放宽过滤器:
roMap.get(postrgesKey) orElse rowMap.get(h2Key) getOrElse "0.0"
我在 Scala 中写了下面的模式匹配:
scala> val rowMap = Map("abc" -> null)
rowMap: scala.collection.immutable.Map[String,Null] = Map(abc -> null)
scala> val postgresKey = "abc"
postgresKey: String = abc
scala> val h2Key = "xyz"
h2Key: String = xyz
建议选项:
scala> (rowMap.get(postgresKey).map(_.asInstanceOf[String]) , rowMap.get(h2Key).map(_.asInstanceOf[String])) match
| {
| case (Some(value), _) if (value != null && value.trim.nonEmpty) => value
| case (None, Some(value)) if (value != null && value.trim.nonEmpty) => value
| case (_ , _) => "0.0"
| }
res9: String = 0.0
我的代码:
scala> (rowMap.get(postgresKey).map(_.asInstanceOf[String]) , rowMap.get(h2Key).map(_.asInstanceOf[String])) match
| {
| case (Some(value), _) if (value != null ) => if (value.trim.nonEmpty) value else "0.0"
| case (None, Some(value)) if (value != null ) => if (value.trim.nonEmpty) value else "0.0"
| case (_ , _) => "0.0"
| }
res10: String = 0.0
建议的选项是否比我的代码有任何性能优势。
我知道 &&
会在看到 null
时停止比较 value.trim.nonEmpty
。因此,我们节省了 1 次比较。
我明白阅读起来也更干净。 建议的方法还有什么更好的吗?
编辑 1:我被告知要避免使用 null
。然而,它是来自 anorm defaultParser
的输入。由于场景难以复制,我举了上面的例子。在我的例子中,单元测试在 H2 中,实际数据库是 postgres。
下面是代码片段:
val anormQuery = SQL(query)
// map the anorm Row as per the input params, differentiate into aggregated and group cols
val tmp = anormQuery.as(anormQuery.defaultParser.*)
logger.info(" Printing the result set object " + tmp.toString())
val finalSqlResultset = tmp.map(row ⇒ {
// Anorm row.asMap has this behaviour that it adds either a leading dot(.) or <tablename>. in front of the map keys (the columns/alias in sql) based on whether it is H2 or Postgres
val rowMap = for ((k, v) ← row.asMap) yield (k, v)
logger.info("Print the resultSet as map : " + rowMap.toString())
val aggregates = expressionsToAggregate.map(input ⇒ {
val (anormKey, postgresKey) = doesColumnHaveAlias.get(input.alias.getOrElse("UnknownAlias")).getOrElse(("Unknown", "Unknown"))
//
val aggResult = AggregatedValue(
input.alias.get,
(rowMap.get(postgresKey).map(_.asInstanceOf[String]), rowMap.get(anormKey).map(_.asInstanceOf[String])) match {
case (Some(value), _) if (value != null && value.trim.nonEmpty) ⇒ value
case (None, Some(value)) if (value != null && value.trim.nonEmpty) ⇒ value
case (_, _) ⇒ "0.0"
})
logger.info("## TRACE 1 ##" + aggResult)
aggResult
})
})
首先,不要使用空值。曾经。
就做 val rowMap = Map("abc" -> "")
.
或者更好的是,根本不要将垃圾放入地图(无论如何你都在过滤掉它!)。
其次,既然你提到了可读性,看起来你正在寻找这样的东西:
rowMap.get(postgresKey).filterNot(_.trim.isEmpty)
.orElse(rowMap.get(h2Key).filerNot(_.trim.isEmpty))
.getOrElse("0.0")
最后,回答您的问题,您显示的两个版本并不相同。 第一个做我上面做的。第二个是这样的:
rowMap.get(postgresKey).orElse(rowMap.get(h2Key))
.filterNot(_.trim.isEmpty)
.getOrElse("0.0")
在前一种情况下,将 postgresKey
设置为 ""
相当于根本不设置它,在这种情况下,您将始终获得 h2Key
的值。
在后一种情况下,如果 postgresKey
设置为 ""
,无论 h2Key
设置的是什么,您都将得到“0.0”。
所以,行为是不同的,这取决于你实际需要哪一个。
如果第一种行为是你需要的,也可以这样写:
Some(rowMap.filterValues(_.trim.nonEmpty)).flatMap { case m =>
m.get(postrgesKey) orElse m.get(h2Key)
}.getOrElse("0.0")
或者,如果您听了我的建议并决定不首先将垃圾放入地图中,您也可以放宽过滤器:
roMap.get(postrgesKey) orElse rowMap.get(h2Key) getOrElse "0.0"