处理不同类型的列表-使用scala(或函数式编程)比Java更昂贵吗?
首先,让我澄清一下,我对Scala和函数式编程非常陌生,因此我的理解和实现可能不正确或效率低下 给定如下所示的文件:处理不同类型的列表-使用scala(或函数式编程)比Java更昂贵吗?,java,scala,functional-programming,Java,Scala,Functional Programming,首先,让我澄清一下,我对Scala和函数式编程非常陌生,因此我的理解和实现可能不正确或效率低下 给定如下所示的文件: type1 param11 param12 ... type2 param21 param22 ... type2 param31 param32 ... type1 param41 param42 ... ... public void parse(List[Type1] type1s, List[Type2] type2s, List[String] lines) {
type1 param11 param12 ...
type2 param21 param22 ...
type2 param31 param32 ...
type1 param41 param42 ...
...
public void parse(List[Type1] type1s, List[Type2] type2s, List[String] lines) {
for (String line in lines) {
if (line.startsWith("type1")) {
Type1 type1 = Type1.createObj(line);
type1s.add(type1)l
} else if (line.startsWith("type2")) {
Type2 type2 = Type2.createObj(line);
type2s.add(type2)l
} else { throw new Exception("Unknown type %s".format(line)) }
}
}
基本上,每一行都以对象的类型开始,该对象可以由同一行中的以下参数创建。我正在使用一个应用程序,它遍历每一行,创建一个给定类型的对象,并返回所有对象的列表
在Java中,我的实现如下:
type1 param11 param12 ...
type2 param21 param22 ...
type2 param31 param32 ...
type1 param41 param42 ...
...
public void parse(List[Type1] type1s, List[Type2] type2s, List[String] lines) {
for (String line in lines) {
if (line.startsWith("type1")) {
Type1 type1 = Type1.createObj(line);
type1s.add(type1)l
} else if (line.startsWith("type2")) {
Type2 type2 = Type2.createObj(line);
type2s.add(type2)l
} else { throw new Exception("Unknown type %s".format(line)) }
}
}
为了在Scala中执行相同的操作,我执行以下操作:
def parse(lines: List[String]): (List[Type1], List[Type2]) = {
val type1Lines = lines filter (x => x.startsWith("type1"))
val type2Lines = lines filter (x => x.startsWith("type2"))
val type1s = type1Lines map (x => Type1.createObj(x))
val type2s = type2Lines map (x => Type2.createObj(x))
(type1s, type2s)
}
据我所知,虽然我的Java实现只遍历了列表一次,但Scala one必须执行三次:过滤type1、过滤type2和从中创建对象。这意味着Scala实现应该比Java实现慢,对吗?此外,Java实现也更节省内存,因为它只有3个实例:type1s
、type2s
和行
。另一方面,Scala one有5条:行
,type1line
,type2line
,type1s
和type2s
因此,我的问题是:
- 有没有更好的方法重新编写Scala实现,使列表只迭代一次
- 使用不可变对象意味着每次都要创建一个新对象,是吗 这意味着函数式编程比其他编程需要更多的内存
1
添加到名为type1s
的列表中,否则,它将2
添加到另一个名为type2s
的列表中
Java实现:
public static void test(List<String> lines) {
System.out.println("START");
List<Integer> type1s = new ArrayList<Integer>();
List<Integer> type2s = new ArrayList<Integer>();
long start = System.currentTimeMillis();
for (String l : lines) {
if (l.startsWith("type1")) {
type1s.add(1);
} else {
type2s.add(2);
}
}
long end = System.currentTimeMillis();
System.out.println(String.format("END after %s milliseconds", end - start));
}
def test(lines: List[String]) = {
println("START")
val start = java.lang.System.currentTimeMillis()
val type1Lines = lines filter (x => x.startsWith("type1"))
val type2Lines = lines filter (x => x.startsWith("type2"))
val type1s = type1Lines map (x => 1)
val type2s = type2Lines map (x => 2)
val end = java.lang.System.currentTimeMillis()
println("END after %s milliseconds".format(end - start))
}
}
Java应用程序平均需要44毫秒,而Scala应用程序则需要200毫秒
object ScalaTester extends App {
val random = new Random
test((0 until 1000000).toList map {_ => s"type${random nextInt 10}"})
def test(lines: List[String]) {
val start = Platform.currentTime
val m = lines groupBy {
case s if s startsWith "type1" => "type1"
case s if s startsWith "type2" => "type2"
case _ => ""
}
println(s"Total type1: ${m("type1").size}; Total type2: ${m("type2").size}; time=${Platform.currentTime - start}")
}
}
Scala(以及一般的函数式编程)的真正优势在于能够处理将一种结构转换为另一种结构的数据。当然,您可以在单个代码行中组合映射、平面映射、过滤器、组等。它导致一次数据收集。
每次都可以一个接一个地创建新集合。这确实会产生一点开销。但是有人在乎吗?尽管您创建了过多的集合,但Scala风格的编程可以帮助您设计面向并行的代码(正如Niklas已经提到的),并防止出现命令式编程容易出现的非常难以捉摸的副作用错误,我认为您应该测试它是否足够快。如果您发现Scala版本太慢,您可以尝试使用单折叠而不是四折叠。如果这仍然很慢,您可以用Java编写一个方法,用Scala编写程序的其余部分。显然,纯数据结构有一定的开销,但通常您并不关心这一点,而是关心在编写代码时它给您带来的生产率提高,以及更容易将程序转换为并行程序的可能性design@NiklasB. 您好,我不是要对我的程序进行基准测试或比较它们的性能,我的意思是问,使用Scala是否会比使用Java产生更多的集合迭代。但是,当性能没有被证明是一个问题时,为什么还要关心它呢?另外,从技术上讲,你没有问它是否会导致多次传球,而是问你是否能在一次传球中做到,你可以,但是,这会使您的代码更难阅读,因此可能不值得费劲。想要在任意大小的文件或列表上迭代一次而不是四次,这是一个合理的愿望,无论绝对性能如何。@DaveNewton:我有一个deja vuHi,非常感谢,看起来您的代码比我的代码性能好得多:)但是,我真的不明白你的结论。这是否意味着使用函数式编程语言会在安全性/可伸缩性增益和性能成本之间进行权衡?当然不是。您可以以完美的性能获得完美、安全和可扩展的代码。这只取决于程序员!))虽然一般来说,由于处理器的命令性质,命令式代码被认为速度要快一点。您知道循环比递归更快,迭代可变集合处理比函数不可变列表遍历更快,等等。。。