1

下面的 Scala 类使用 JDOM 解析文件并将文件中的值填充到 Scala 不可变 Map 中。在 Map 上使用+运算符似乎没有任何效果,因为 Map 始终为零。

import java.io.File
import org.jsoup.nodes.Document
import org.jsoup.Jsoup
import org.jsoup.select.Elements
import org.jsoup.nodes.Element
import scala.collection.immutable.TreeMap

class JdkElementDetail() {

  var fileLocation: String = _

  def this(fileLocation: String) = {
      this()
      this.fileLocation = fileLocation;
    }


  def parseFile : Map[String , String] = {

    val jdkElementsMap: Map[String, String] = new TreeMap[String , String];
    val input: File = new File(fileLocation);
    val doc: Document = Jsoup.parse(input, "UTF-8", "http://example.com/");
    val e: Elements = doc.getElementsByAttribute("href");

    val href: java.util.Iterator[Element] = e.iterator();
    while (href.hasNext()) {
      var objectName = href.next();
      var hrefValue = objectName.attr("href");
      var name = objectName.text();

      jdkElementsMap + name -> hrefValue
            println("size is "+jdkElementsMap.size)
    }

    jdkElementsMap
  }

}

println("size is "+jdkElementsMap.size) always prints "size is 0"

为什么大小总是零,我没有正确添加到地图中吗?

唯一的解决方法是转换jdkElementsMap为 avar然后使用以下内容吗?

jdkElementsMap += name -> hrefValue

在这里删除 while 循环是我更新的对象:

package com.parse

import java.io.File
import org.jsoup.nodes.Document
import org.jsoup.Jsoup
import org.jsoup.select.Elements
import org.jsoup.nodes.Element
import scala.collection.immutable.TreeMap
import scala.collection.JavaConverters._

class JdkElementDetail() {

  var fileLocation: String = _

  def this(fileLocation: String) = {
      this()
      this.fileLocation = fileLocation;
    }


  def parseFile : Map[String , String] = {

    var jdkElementsMap: Map[String, String] = new TreeMap[String , String];
    val input: File = new File(fileLocation);
    val doc: Document = Jsoup.parse(input, "UTF-8", "http://example.com/");
    val elements: Elements = doc.getElementsByAttribute("href");

    val elementsScalaIterator = elements.iterator().asScala

    elementsScalaIterator.foreach {
      keyVal => {
          var hrefValue = keyVal.attr("href");
          var name = keyVal.text();
          println("size is "+jdkElementsMap.size)
          jdkElementsMap += name -> hrefValue
       }
    }
    jdkElementsMap
  }

}
4

2 回答 2

7

不可变的数据结构——无论是列表还是映射——就是这样:不可变的。您永远不会更改它们,而是根据对旧数据结构的更改创建新的数据结构。

如果你这样做val x = jdkElementsMap + (name -> hrefValue)了,那么你会得到新的地图x,而jdkElementsMap仍然是一样的。

如果你jdkElementsMap变成a var,那么你可以做jdkEleemntsMap = jdkElementsMap + (name -> hrefValue),或者只是jdkElementsMap += (name -> hrefValue)。后者也适用于可变地图。

这是唯一的方法吗?不,但你必须放弃while循环来实现同样的目标。您可以替换这些行:

val href: java.util.Iterator[Element] = e.iterator();
while (href.hasNext()) {
  var objectName = href.next();
  var hrefValue = objectName.attr("href");
  var name = objectName.text();

  jdkElementsMap + name -> hrefValue
        println("size is "+jdkElementsMap.size)
}

jdkElementsMap

带有折叠,例如:

import scala.collection.JavaConverters.asScalaIteratorConverter

e.iterator().asScala.foldLeft(jdkElementsMap) {
  case (accumulator, href) =>  // href here is not an iterator
    val objectName = href
    val hrefValue = objectName.attr("href")
    val name = objectName.text()

    val newAccumulator = accumulator + (name -> hrefValue)

    println("size is "+newAccumulator.size)

    newAccumulator
}

或递归:

def createMap(hrefIterator: java.util.Iterator[Element],
              jdkElementsMap: Map[String, String]): Map[String, String] = {
  if (hrefIterator.hasNext()) {
    val objectName = hrefIterator.next()
    val hrefValue = objectName.attr("href")
    val name = objectName.text()

    val newMap = jdkElementsMap + name -> hrefValue

    println("size is "+newMap.size)

    createMap(hrefIterator, newMap)
  } else {
     jdkElementsMap
  }
}

createMap(e.iterator(), new TreeMap[String, String])

性能方面,折叠会比较慢,递归应该会稍微快一点。

请注意,Scala 确实提供了可变映射,而不仅仅是为了能够说它拥有它们:如果它们更适合您的问题,那么继续使用它们!如果您想学习如何使用不可变的方法,那么上面的两种方法是您应该学习的方法。

于 2013-01-30T23:33:06.927 回答
3

该地图是不可变的,因此任何修改都将返回修改后的地图。jdkElementsMap + (name -> hrefValue)返回一个包含新对的新地图,但在创建后您将丢弃修改后的地图。

编辑:看起来您可以将 Java 可迭代对象转换为 Scala 可迭代对象,因此您可以折叠生成的序列并累积映射:

import scala.collection.JavaConverters._
val e: Elements = doc.getElementsByAttribute("href");
val jdkElementsMap = e.asScala
    .foldLeft(new TreeMap[String , String])((map, href) => map + (href.text() -> href.attr("href"))

如果你不关心你创建什么样的地图,你可以使用toMap

val jdkElementsMap = e.asScala
    .map(href => (href.text(), href.attr("href")))
    .toMap
于 2013-01-30T22:52:16.260 回答