连接包含在一组对象中的集合,在scala中功能上

时间:2011-05-13 11:27:24

标签: scala scala-collections

我刚开始使用scala,我正在将一些java代码转换为scala,并尝试使其更好,功能更优雅。

我有以下代码,其中包含一个方法(getRequiredUploadPathKeys),它提供了MyClass中所有可用路径模板所需的所有路径键的并集。

trait PathTemplate {
    def getRequiredPathKeys:Set[PathKey]
}

class MyClass(accessPaths:Set[PathTemplate], targetPath:PathTemplate){

    def getAllRequiredPathKeys: Set[PathKey] = {
        val pathKeys = HashSet[PathKey]()
        pathKeys.addAll(targetPath.getRequiredPathKeys)

        for (accessTemp <- accessPaths) {
            pathKeys.addAll(accessTemp.getRequiredPathKeys)
        }
        return pathKeys
    }
}

这个方法似乎在scala中更加简洁。任何人都可以指出我如何去那里吗?

谢谢, 保罗

3 个答案:

答案 0 :(得分:6)

def getAllRequiredPathKeys = 
  (accessPaths + targetPath).flatMap(_.getRequiredPathKeys)

或者用于

def getAllRequiredPathKeys = 
  for(path <- accessPaths + targetPath; 
      key <- path.getRequiredPathKeys) yield key

答案 1 :(得分:6)

这里奇怪的是你正在使用 Java的 HashSet - Scala中没有addAll - 并且不需要它。

所以我将评论各种设计决策以及它们与Java到Scala的区别。首先,

    val pathKeys = HashSet[PathKey]()

惯用语Scala通常不会引用实现接口的类,因为接口有一个工厂。所以你通常只使用Set[PathKey]()。此外,这显然是Scala工厂,因为没有new关键字 - 这意味着其余的代码不起作用,因为Scala的addAll中没有Set

    pathKeys.addAll(targetPath.getRequiredPathKeys)

这里你不直接使用targetPath.getRequiredPathKeys,因为Java的Set是可变的。默认的Scala Set是不可变的,这使得它无用 - 您只需使用targetPath.getRequirePathKeys作为基本集,而无需将其元素添加到另一个集合。

性能方面,类似Scala的函数式语言 - 在其不可变集合实现中使用 persistent datastructures 。这意味着它重用一个数据结构的一部分来创建派生数据结构,而不是每次创建新数据结构时都复制所有内容。这是唯一可能的,因为不变性保证。

无论如何,你可以这样做:

val pathKeys = targetPath.getRequiredPathKeys

接下来,

    for (accessTemp <- accessPaths) {
        pathKeys.addAll(accessTemp.getRequiredPathKeys)
    }

首先,理解的惯用用法是返回一个值,即for-yield。上述用法应仅限于代码的副作用部分。更清楚的是,上述步骤可以分两步完成:

    // Get all required Path Keys
    val requiredPathKeys = for {
        accessPath <- accessPaths
        requiredPathKey <- accessPath.getRequiredPathKeys
    } yield requiredPathKey

    // Then add them
    pathKeys.addAll(requiredPathKeys)

但是,鉴于您没有addAll填写Scala的Set,您需要将pathKeys设为var,或使用可变数据结构,或只是改变顺序:

    val requiredPathKeys = for {
        accessPath <- accessPaths
        requiredPathKey <- accessPath.getRequiredPathKeys
    } yield requiredPathKey

    val pathKeys = targetPath.getRequiredPathKeys ++ requiredPathKeys

但请注意getRequiredPathKeys的重复。功能程序员厌恶重复。在我看来,有时过于如此,但在这种情况下,它可以很容易地删除:

    val pathKeys = for {
        accessPath <- accessPaths + targetPath
        requiredPathKey <- accessPath.getRequiredPathKeys
    } yield requiredPathKey

上述代码表明了另一项改进。如果您看到this question有关Scala的理解,您会发现以上内容相当于

    val pathKeys = (accessPaths + targetPath).flatMap( accessPath =>
        accessPath.getRequiredPathKeys.map( requiredPathKey => requiredPathKey ))

最后的map是多余的,因为它没有做任何事情。实际上,只要yield只返回一个简单的标识符,表达式中就会有一个冗余的map。删除它给我们:

    val pathKeys = (accessPaths + targetPath).flatMap(accessPath =>
        accessPath.getRequiredPathKeys)

或者,使用Scala的匿名函数参数语法

    val pathKeys = (accessPaths + targetPath).flatMap(_.getRequiredPathKeys)

最后,

    return pathKeys

Scala中的关键字return用于指定工作流中的异常 - 该方法的执行被中止而不是一直到最后。并非这不是没有用,但是,就像例外本身一样,它不应该没有原因使用。在这里,您应该使用

    pathKeys

但是,此时,您的代码只是这两行:

    val pathKeys = (accessPaths + targetPath).flatMap(_.getRequiredPathKeys)
    pathKeys

这使得val任务完全减少了。因此,您可以将其减少为:

    (accessPaths + targetPath).flatMap(_.getRequiredPathKeys)

因此方法变为

def getAllRequiredPathKeys: Set[PathKey] = {
    (accessPaths + targetPath).flatMap(_.getRequiredPathKeys)
}

现在,我知道我说“终于”回到了那里,但是还有一个Scala惯例可以应用。每当一个方法包含单个表达式而不是多个语句时,约定就是省略大括号。换句话说,这样做:

def getAllRequiredPathKeys: Set[PathKey] =
    (accessPaths + targetPath).flatMap(_.getRequiredPathKeys)

或者,如果空间允许,将所有内容放在一行上。实际上,如果你删除了getAllRequiredPathKeys类型,那么它就很合适了。另一方面,鼓励使用公共方法的显式返回类型。

因此,这将为您提供Scala的不可变Set。假设您的输入和输出必须是Java的Set。在这种情况下,我没有看到任何可以简化代码的事情,除非你愿意在Java和Scala集之间进行转换。你可以这样做:

trait PathTemplate {
    def getRequiredPathKeys:java.util.Set[PathKey]
}

import scala.collection.JavaConverters._

class MyClass(accessPaths:java.util.Set[PathTemplate], targetPath:PathTemplate){
    def getAllRequiredPathKeys: java.util.Set[PathKey] =
        (accessPaths.asScala + targetPath).flatMap(_.getRequiredPathKeys.asScala).asJava
}

然而,这使用了Scala的 mutable Set,这意味着创建新Set的每个操作都将复制旧Set的所有内容。< / p>

答案 2 :(得分:4)

你的意思是:

def getAllRequiredPathKeys = (accessPaths map { _.getRequiredPathKeys }).flatten ++ targetPath.getRequiredPathKeys