18

我想使用java.time.LocalDatejava.time.LocalDateTime隐含Ordered的:

val date1 = java.time.LocalDate.of(2000, 1, 1)
val date2 = java.time.LocalDate.of(2010, 10, 10)
if (date1 < date2) ...

import scala.math.Ordering.Implicits._不起作用,因为LocalDate继承自Comparable<ChronoLocalDate>而不是Comparable<LocalDate>. 如何编写自己的隐式 Orderd 以使用 <、<=、>、>= 运算符/方法来比较LocalDate's?

编辑:

我找到了一种使用隐式类的方法:

import java.time.{LocalDate}

object MyDateTimeUtils {
  implicit class MyLocalDateImprovements(val ld: LocalDate)
           extends Ordered[LocalDate] {
    def compare(that: LocalDate): Int = ld.compareTo(that)
  }
}

// Test

import MyDateTimeUtils._

val d1 = LocalDate.of(2016, 1, 1)
val d2 = LocalDate.of(2017, 2, 3)

if (d1 < d2) println("d1 is less than d2")

但我更喜欢 Scala 为所有实现Comparable<T>. 你只需要 import scala.math.Ordering.Implicits._在你的代码中。Scala 实现它的方式如下:

implicit def ordered[A <% Comparable[A]]: Ordering[A] = new Ordering[A] {
  def compare(x: A, y: A): Int = x compareTo y
}

但不幸的是LocalDate实现Comparable<ChronoLocalDate>而不是Comparable<LocalDate>. 我找不到修改上述隐式有序方法以适应LocalDate/的方法Comparable<ChronoLocalDate>。任何想法?

4

5 回答 5

26

您可以使用Ordering.by为任何类型创建排序,给定从该类型到已经具有排序的函数 - 在这种情况下,到Long

implicit val localDateOrdering: Ordering[LocalDate] = Ordering.by(_.toEpochDay)
于 2016-06-27T17:11:20.727 回答
24

比较LocalDate.toEpochDay很清楚,虽然可能相对较慢......

@tzach-zohar的回答很棒,因为最明显的是发生了什么;您在 Epoch Day 之前订购:

implicit val localDateOrdering: Ordering[LocalDate] = Ordering.by(_.toEpochDay)

但是,如果您查看实现,toEpochDay您会发现它相对复杂 - 18 行代码,有 4 个除法,3 个条件和一个调用isLeapYear()- 结果值没有被缓存,因此每次比较都会重新计算,如果LocalDate要排序的 s 数量很大,这可能会很昂贵。

...利用LocalDate.compareTo可能更高效...

实现LocalDate.compareTo更简单——只有 2 个条件,没有除法——这就是你通过隐式转换java.lang.Comparablescala.math.Ordering那个scala.math.Ordering.Implicits._提议所得到的,只要它有效!但正如你所说,它没有,因为LocalDate继承自Comparable<ChronoLocalDate>而不是Comparable<LocalDate>. 一种利用它的方法可能是......

import scala.math.Ordering.Implicits._

implicit val localDateOrdering: Ordering[LocalDate] =
  Ordering.by(identity[ChronoLocalDate])

...这让您可以LocalDate通过将 s 转换为s 来订购 s ChronoLocalDate,并使用Ordering[ChronoLocalDate]scala.math.Ordering.Implicits._您的 s !

...最后它看起来最好使用 SAM 类型的 lambda 语法

SAM 类型的lambda 语法,随 Scala 2.12 引入,可以使构造一个非常短的工作new Ordering

implicit val localDateOrdering: Ordering[LocalDate] = _ compareTo _

...我认为这最终成为我个人的最爱!简洁,仍然相当清晰,并且使用(我认为)表现最好的比较方法。

于 2018-05-17T15:56:23.813 回答
4

对隐式稍作修改ordered应该可以解决问题。

type AsComparable[A] = A => Comparable[_ >: A]

implicit def ordered[A: AsComparable]: Ordering[A] = new Ordering[A] {
  def compare(x: A, y: A): Int = x compareTo y
}

现在,与自身或自身超类型可比较的每个类型都应该有一个Ordering.

于 2017-10-18T13:41:35.020 回答
3

这是我使用的解决方案:

定义两个隐式。第一个用于Ordering[LocalDate]提供可用的。第二个用于提供非常方便LocalDatecompare方法。我通常将它们放在库中的包对象中,我可以将它们包含在我需要的地方。

package object net.fosdal.oslo.odatetime {

  implicit val orderingLocalDate: Ordering[LocalDate] = Ordering.by(d => (d.getYear, d.getDayOfYear))

  implicit class LocalDateOps(private val localDate: LocalDate) extends AnyVal with Ordered[LocalDate] {
    override def compare(that: LocalDate): Int = Ordering[LocalDate].compare(localDate, that)
  }
}

定义了这两个之后,您现在可以执行以下操作:

import net.fosdal.oslo.odatetime._

val bool: Boolean = localDate1 < localDate1

val localDates: Seq[LocalDate] = ...
val sortedSeq = localDates.sorted

或者......您可以直接使用我的库(v0.4.3)。见:https ://github.com/sfosdal/oslo

于 2018-03-01T00:33:35.930 回答
2

这是我的解决方案java.time.LocalDateTime

implicit val localDateTimeOrdering: Ordering[LocalDateTime] =
  Ordering.by(x => x.atZone(ZoneId.of("UTC")).toEpochSecond)
于 2019-03-11T06:38:14.047 回答