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
转换为var
然后使用以下内容吗?
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
}
}
答案 0 :(得分:7)
不可变数据结构 - 无论是列表还是映射 - 只是:不可变的。您永远不会更改它们,您可以根据对旧数据结构的更改创建 new 数据结构。
如果您执行val x = jdkElementsMap + (name -> hrefValue)
,那么您将在x
上获得新地图,而jdkElementsMap
仍然是相同的。
如果您将jdkElementsMap
更改为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确实提供了可变的地图,而不仅仅是说它有它们:如果它们更适合您的问题,那么请继续使用它们!如果你想学习如何使用不可变的那些,那么上面的两种方法就是你应该学习的方法。
答案 1 :(得分: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