简体   繁体   English

将Java TreeMap代码迁移到Scala?

[英]Migrating Java TreeMap code to Scala?

I am migrating my Java code base to pure Scala and I am stuck on this one piece of code . 我正在将我的Java代码库迁移到纯Scala,我仍然坚持使用这一段代码 I have an implementation of an IntervalMap ie a data structures that let's you efficiently map ranges [from,to] to values where the set , delete and get operations are all O(log n) (slightly different from an IntervalTree or a SegmentTree). 我有一个让你有效地映射范围的IntervalMap即数据结构的实现[from,to]valuessetdeleteget操作都是O(log n) (从IntervalTree或线段树略有不同)。

This code uses Java's java.util.TreeMaps and while migrating to Scala, I ran into 2 big issues: 这段代码使用Java的java.util.TreeMaps ,在迁移到Scala时,我遇到了两个大问题:

  1. Scala has no mutable.TreeMap - I decided to go around it by using mutable.TreeSet (oddly Scala has mutable.TreeSet but no mutable.TreeMap ) for storing the keys and storing the values in an auxiliary mutable.Map . Scala没有mutable.TreeMap - 我决定使用mutable.TreeSet (奇怪的是Scala有mutable.TreeSet但没有mutable.TreeMap )来存储密钥并将值存储在辅助的mutable.Map This is an unpleasant hack but is there any better way? 这是一个不愉快的黑客,但还有更好的方法吗?

  2. Next problem is Scala's mutable.TreeSet has no equivalent of java.util.TreeSet 's ceilingKey , floorEntry , pollFirst , pollLast which are all O(log n) operations in Java. 下一个问题是Scala的mutable.TreeSet没有java.util.TreeSetceilingKeyfloorEntrypollFirstpollLast等同于Java中的所有O(log n)操作。

So, how can I best migrate my code to Scala? 那么,我怎样才能最好地将我的代码迁移到Scala? What are the best practices in these situations? 这些情况下的最佳做法是什么? I really do not want to write my own tree implementations. 我真的不想编写自己的树实现。 Is there a more idiomatic Scala way of writing IntervalMaps that I am not aware of? 有没有更惯用的Scala编写IntervalMaps的方式,我不知道? Or is there some reputable library out there? 或者那里有一些有信誉的图书馆? Or does Scala just plain suck here with its gimped TreeSet and non-existent TreeMaps. 或者Scala只是简单地用它的gimped TreeSet和不存在的TreeMaps来吸吮。 Ofcourse I can just use Java's TreeMap in Scala but that is ugly and I lose all the nice Scala collection features and I might as well use Java then. 当然我可以在Scala中使用Java的TreeMap ,但这很难看,我失去了所有不错的Scala集合功能,我不妨使用Java。

Here is my current Java code: https://gist.github.com/pathikrit/5574521 这是我目前的Java代码: https//gist.github.com/pathikrit/5574521

The answer is, unfortunately, to just use the Java TreeMap class. 不幸的是,答案只是使用Java TreeMap类。

Scala doesn't have its own copy of everything, and this is one of the most notable exceptions. Scala没有自己的所有副本,这是最值得注意的例外之一。 One of the reasons it's Java-compatible is so that you don't have to re-invent every wheel. 与Java兼容的原因之一是您不必重新发明每个轮子。

The reason you still want to use Scala is that not every bit of code you write is about this TreeMap . 您仍然希望使用Scala的原因是您编写的所有代码都不是关于此TreeMap的 Your IntervalMap can be a Scala IntervalMap ; 您的IntervalMap可以是Scala IntervalMap ; you just use the Java TreeMap internally to implement it. 您只需在内部使用Java TreeMap来实现它。 Or you could use the immutable version in Scala, which now performs reasonably well for an immutable version. 或者你可以在Scala中使用不可变版本,它现在对于不可变版本表现得相当不错。

Perhaps in 2.11 or 2.12 there will be a mutable TreeMap ; 也许在2.11或2.12中会有一个可变的TreeMap ; it requires someone to write it, test it, optimize it, etc., but I don't think there's a philosophical objection to having it. 它需要有人写它,测试它,优化它等等,但我不认为有这个有哲学上的反对意见。

1) What's the problem with using an immutable TreeMap internally? 1)内部使用不可变TreeMap有什么问题? It's more or less just as efficient as mutable tree map, does everything in O(log n). 它或多或少与可变树映射一样高效,在O(log n)中执行所有操作。

2) In the Scala version, there is no ceilingKey and floorKey , but instead one has methods from and to that do essentially the same, but return a whole subtree instead of single entries. 2)在Scala版本中,没有ceilingKeyfloorKey ,而是一个具有fromto方法基本相同,但返回整个子树而不是单个条目。

Full 1:1 port of Java-code: 完整的1:1 Java代码端口:

import scala.collection._
import scala.collection.immutable.TreeMap

case class Segment[T](start: Int, end: Int, value: T) {
  def contains(x: Int) = (start <= x) && (x < end)
  override def toString = "[%d,%d:%s]".format(start, end, value)
}

class IntervalMap[T] {
  private var segments = new TreeMap[Int, Segment[T]]
  private def add(s: Segment[T]): Unit = segments += (s.start -> s)
  private def destroy(s: Segment[T]): Unit = segments -= s.start
  def ceiling(x: Int): Option[Segment[T]] = {
    val from = segments.from(x)
    if (from.isEmpty) None
    else Some(segments(from.firstKey))
  }
  def floor(x: Int): Option[Segment[T]] = {
    val to = segments.to(x)
    if (to.isEmpty) None
    else Some(segments(to.lastKey))
  }
  def find(x: Int): Option[Segment[T]] = {
    floor(x).filter(_ contains x).orElse(ceiling(x))
  }

  // This is replacement of `set`, used as myMap(s,e) = v
  def update(x: Int, y: Int, value: T): Unit = {
    require(x < y)
    find(x) match {
      case None => add(Segment[T](x, y, value))
      case Some(s) => {
        if (x < s.start) {
          if (y <= s.start) {
            add(Segment[T](x, y, value))
          } else if (y < s.end) {
            destroy(s)
            add(Segment[T](x, y, value))
            add(Segment[T](y, s.end, s.value))
          } else {
            destroy(s)
            add(Segment[T](x, s.end, value))
            this(s.end, y) = value
          }
        } else if (x < s.end) {
          destroy(s)
          add(Segment[T](s.start, x, s.value))
          if (y < s.end) {
            add(Segment[T](x, y, value))
            add(Segment[T](y, s.end, s.value))
          } else {
            add(Segment[T](x, s.end, value))
            this(s.end, y) = value
          }
        } else {
          throw new IllegalStateException
        }
      }
    }
  }

  def get(x: Int): Option[T] = {
    for (seg <- floor(x); if (seg contains x)) yield seg.value
  }

  def apply(x: Int) = get(x).getOrElse{
    throw new NoSuchElementException(
      "No value set at index " + x
    )
  }

  override def toString = segments.mkString("{", ",", "}")
}

// little demo
val m = new IntervalMap[String]
println(m)
m(10, 20) = "FOOOOOOOOO"
m(18, 30) = "_bar_bar_bar_"
m(5, 12) = "bazzz"
println(m)

for (x <- 1 to 42) printf("%3d -> %s\n",x,m.get(x))

Result: 结果:

{}
{5 -> [5,12:bazzz],12 -> [12,18:FOOOOOOOOO],18 -> [18,20:_bar_bar_bar_],20 -> [20,30:_bar_bar_bar_]}
  1 -> None
  2 -> None
  3 -> None
  4 -> None
  5 -> Some(bazzz)
  6 -> Some(bazzz)
  7 -> Some(bazzz)
  8 -> Some(bazzz)
  9 -> Some(bazzz)
 10 -> Some(bazzz)
 11 -> Some(bazzz)
 12 -> Some(FOOOOOOOOO)
 13 -> Some(FOOOOOOOOO)
 14 -> Some(FOOOOOOOOO)
 15 -> Some(FOOOOOOOOO)
 16 -> Some(FOOOOOOOOO)
 17 -> Some(FOOOOOOOOO)
 18 -> Some(_bar_bar_bar_)
 19 -> Some(_bar_bar_bar_)
 20 -> Some(_bar_bar_bar_)
 21 -> Some(_bar_bar_bar_)
 22 -> Some(_bar_bar_bar_)
 23 -> Some(_bar_bar_bar_)
 24 -> Some(_bar_bar_bar_)
 25 -> Some(_bar_bar_bar_)
 26 -> Some(_bar_bar_bar_)
 27 -> Some(_bar_bar_bar_)
 28 -> Some(_bar_bar_bar_)
 29 -> Some(_bar_bar_bar_)
 30 -> None
 31 -> None
 32 -> None
 33 -> None
 34 -> None
 35 -> None

The Segment class should be set private[yourPackage] , some documentation should be added. Segment类应设置为private[yourPackage] ,应添加一些文档。

It seems like you want to use the nice Scala collections features. 看起来你想要使用漂亮的Scala集合功能。 I don't think you need to reimplement your class. 我认为你不需要重新实现你的课程。

Have you seen scala.collection.JavaConversions ? 你见过scala.collection.JavaConversions吗?

You might follow a similar approach with a wrapper and then implement the methods you want accordingly. 您可以使用包装器遵循类似的方法,然后相应地实现您想要的方法。 You might need to be more creative with how you define and then use methods unique to your kind of map, but shouldn't be a big deal. 您可能需要更具创造性,如何定义,然后使用您的地图类型独有的方法,但不应该是一个大问题。

I hope this gives you an idea. 我希望这会给你一个想法。 Let me know if you need more guidance and I could help you out (it looks like it's been a while since you asked). 如果你需要更多的指导我可以告诉我,我可以帮助你(看起来你已经有一段时间了)。

Scala 2.12最近有了mutable.TreeMaphttps//github.com/scala/scala/pull/4504

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM