我不认为使用 Java 类有什么特别的错误,这些类被设计为以命令式的方式按照它们设计的方式工作。惯用的 Scala 包括能够按照预期使用惯用的 Java,即使样式确实有点冲突。
但是,如果您想要(也许作为练习,或者也许因为它确实稍微澄清了逻辑)以更实用的无变量方式执行此操作,您可以这样做。在2.8中,它特别好,所以即使你使用2.7.7,我也会给出2.8的答案。
首先,我们需要设置问题,但你并没有完全解决这个问题,但让我们假设我们有这样的问题:
import java.io._
import java.util.zip._
import scala.collection.immutable.Stream
val fos = new FileOutputStream("new.zip")
val zipOut = new ZipOutputStream(new BufferedOutputStream(fos))
val zipIn = new ZipInputStream(new FileInputStream("old.zip"))
def entryIsValid(ze: ZipEntry) = !ze.isDirectory
现在,我们要复制 zip 文件。我们可以使用的技巧是continually
中的方法collection.immutable.Stream
。它的作用是为您执行一个延迟评估循环。然后,您可以获取并过滤结果以终止并处理您想要的内容。当您想要将某些东西作为迭代器但事实并非如此时,这是一种方便使用的模式。 (如果该项目自行更新,您可以使用.iterate
in Iterable
or Iterator
--这通常更好。)这是这种情况的应用程序,使用两次:一次用于获取条目,一次用于读取/写入数据块:
val buffer = new Array[Byte](1024)
Stream.continually(zipIn.getNextEntry).
takeWhile(_ != null).filter(entryIsValid).
foreach(entry => {
zipOut.putNextEntry(new ZipEntry("subdir/"+entry.getName))
Stream.continually(zipIn.read(buffer)).takeWhile(_ != -1).
foreach(count => zipOut.write(buffer,0,count))
})
}
zipIn.close
zipOut.close
密切关注.
在某些行的末尾!我通常会把它写成一长行,但最好将其换行,这样您就可以在这里看到所有内容。
以防万一不清楚,让我们解开其中一个用途continually
.
Stream.continually(zipIn.read(buffer))
这要求继续打电话zipIn.read(buffer)
根据需要多次,存储结果的整数。
.takeWhile(_ != -1)
这指定了需要多少次,返回一个不定长度的流,但当它遇到一个时就会退出-1
.
.foreach(count => zipOut.write(buffer,0,count))
这会处理流,依次获取每个项目(计数),并使用它来写入缓冲区。这以一种有点偷偷摸摸的方式工作,因为你依赖于这样一个事实:zipIn
刚刚被调用以获取流的下一个元素 - 如果您尝试再次执行此操作,而不是在一次通过流中执行此操作,则会失败,因为buffer
会被覆盖。但这里没关系。
所以,这就是:一个稍微更紧凑、可能更容易理解、可能不太容易理解但功能更强大的方法(尽管仍然存在大量副作用)。相比之下,在 2.7.7 中,我实际上会以 Java 方式执行此操作,因为Stream.continually
不可用,并且构建自定义的开销Iterator
对于这一个案例来说不值得。 (但是,如果我要进行更多 zip 文件处理并且可以重用代码,那就值得了。)
编辑:寻找可用的归零方法对于检测 zip 文件的末尾有点不稳定。我认为“正确”的方法是等到你得到一个null
从...回来getNextEntry
。考虑到这一点,我编辑了之前的代码(有一个takeWhile(_ => zipIn.available==1)
现在是一个takeWhile(_ != null)
)并在下面提供了一个基于 2.7.7 迭代器的版本(请注意,一旦完成定义迭代器的工作,主循环有多小,这确实使用了 vars):
val buffer = new Array[Byte](1024)
class ZipIter(zis: ZipInputStream) extends Iterator[ZipEntry] {
private var entry:ZipEntry = zis.getNextEntry
private var cached = true
private def cache { if (entry != null && !cached) {
cached = true; entry = zis.getNextEntry
}}
def hasNext = { cache; entry != null }
def next = {
if (!cached) cache
cached = false
entry
}
}
class DataIter(is: InputStream, ab: Array[Byte]) extends Iterator[(Int,Array[Byte])] {
private var count = 0
private var waiting = false
def hasNext = {
if (!waiting && count != -1) { count = is.read(ab); waiting=true }
count != -1
}
def next = { waiting=false; (count,ab) }
}
(new ZipIter(zipIn)).filter(entryIsValid).foreach(entry => {
zipOut.putNextEntry(new ZipEntry("subdir/"+entry.getName))
(new DataIter(zipIn,buffer)).foreach(cb => zipOut.write(cb._2,0,cb._1))
})
zipIn.close
zipOut.close