java scala
Scala使用与Java完全兼容并在JVM上运行的简洁语法,将面向对象和功能编程范例结合在一起。 支持函数式编程风格,尤其是现在不希望在2012年JavaSE 8之前不将其添加到Java中的Lambda表达式,可以帮助减少您需要编写的样板代码的数量,可以说使得集中精力更加容易在手头的任务上。 本文介绍了Scala。
要开始使用,只需安装最新Scala发行牛逼 ypesafe栈 ,打开命令提示符,然后键入“阶”:这将启动REPL(读-EVAL打印循环)交互式编程环境。 现在您可以输入您的第一个Scala行:
scala> val哥伦布:Int = 1492
哥伦布:国际= 1492
我们刚刚声明了一个Int类型的变量,其值为1492,就像我们在Java中使用“ Int columbus = 1492;”所做的那样。 这里的区别与将类型放在Scala中的变量后面的反向语法不同,在于“ val”将变量明确声明为不可变。 如果我们尝试修改它:
scala> columbus = 1500
<console>:8: error: reassignment to val
columbus = 1500
^
注意消息是如何精确显示错误所在的行。 尝试再次声明该变量,但是这次将其声明为“ var”以使其可变。 顺便说一下,编译器足够聪明,可以知道1492是整数,因此您根本不需要指定类型:
scala> var columbus = 1492
columbus: Int = 1492
scala> columbus = 1500
columbus: Int = 1500
继续,让我们定义一个类:
scala> case class Employee( name:String="guest", age:Int=30, company:String =
"DevCode" )
defined class Employee
我们定义了一个Employee类,它具有3个不可变的字段,名称,年龄和公司均为默认值。 “ case”一词类似于Java中的switch语句,但更为灵活。 这意味着该类具有一种额外的模式匹配机制以及其他功能,其中包括一种用于创建实例的工厂方法(无需使用“ new”关键字来创建实例)。 同样,也无需创建默认的吸气剂。 与Java不同,变量默认情况下是公共的(不受保护),并且Scala为以变量本身名称命名的公共变量创建一个getter。 如果需要,可以通过在参数前面使用“ var”来使字段可变和/或私有(例如,案例类Person(private var name:String))。
让我们以不同的方式创建一些实例以展示各种功能,例如命名和默认参数(从Scala 2.8开始可用):
scala> val guest = Employee()
guest: Employee = Employee(guest,30,DevCode)
scala> val guestAge = guest.age // (the default getter for the age variable)
guestAge: Int = 300
scala> val anna = Employee("Anna")
anna: Employee = Employee(Anna,30,DevCode)
scala> val thomas = Employee("Thomas",41)
thomas: Employee = Employee(Thomas,41,DevCode)
scala> val luke = Employee("Luke", company="LucasArt")
luke: Employee = Employee(Luke,30,LucasArt)
scala> val yoda = luke.copy("Yoda", age=800)
yoda: Employee = Employee(Yoda,800,LucasArt)
但是,以下
scala> val darth = Employee("Darth", "DevCode")
<console>:9: error: type mismatch;
found : java.lang.String("DevCode")
required: Int
Error occurred in an application involving default arguments.
val darth = Employee("Darth", "DevCode")
^
...不起作用(不是因为在DevCode上没有使用Darth!),而是因为构造函数期望在该位置使用age参数,因为该参数未明确命名。
现在,我们将继续进行收藏,因为那是真正令人兴奋的地方。
使用泛型(从Java 5开始),例如,Java可以通过编写以下代码来遍历整数列表:
List<Integer> numbers = new arrayList<Integer>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
for(Integer n:numbers) {
System.out.println("Number "+n);
}
产生
1号
2号
3号
Scala集合系统地区分不可变集合和可变集合,但默认情况下通过构造不可变集合来鼓励不可变。 它们通过从此类操作返回新集合而不是对其进行修改来模拟添加,更新或删除。
可以编写与以前的Java代码等效的Scala代码:
scala> val numbers = List(1,2,3)
numbers: List[Int] = List(1, 2, 3)
scala> for (n <- numbers) println("Number "+n)
Number 1
Number 2
Number 3
这种“ for”循环构造非常类似于Java的命令式编程风格。 用Scala(以及JVM上的许多语言,例如Groovy,JRuby或Jython)和Scala编写它的另一种方法涉及使用lambda表达式(有时称为闭包)的更具功能性的样式。 简而言之,Lambda只是可以作为参数传递的函数。 这些函数将参数作为输入(在我们的示例中为“ n”整数),并返回其主体的最后一条语句作为其结果。 它们的形式
functionName { input =>
body
} scala> numbers.foreach { n:Int => // just press Enter to continue on the next line
| println("Number "+n)
| }
Number 1
Number 2
Number 3
在这种情况下,主体只有一个语句(println ...),因此返回Unit,即“空结果”与Java中的void大致等效,但“ void”不返回任何内容。
假设我们要操纵和转换元素,而不仅仅是输出数字列表。 在这种情况下,我们希望调用将产生结果列表的方法,以便以后使用。 让我们尝试一些例子:
scala> val reversedList = numbers.reverse
reversedList: List[Int] = List(3, 2, 1)
scala> val numbersLessThan3 = numbers.filter { n => n < 3 }
numbersLessThan3: List[Int] = List(1, 2)
scala> val oddNumbers = numbers.filterNot { n => n % 2 == 0 }
oddNumbers: List[Int] = List(1, 3)
scala> val higherNumbers = numbers.map { n => n + 10 }
higherNumbers: List[Int] = List(11, 12, 13)
最后的转换“地图”非常有用; 它将闭包应用于列表的每个元素,其结果是一个包含每个转换元素的大小相同的列表。
我们在这里要介绍的最后一种方法是“ foldLeft”方法,该方法将状态从一个元素传播到另一个元素。 例如,要对列表中的元素求和,您需要对其进行累加并跟踪从一个元素到下一个元素的中间计数器:
scala> val sumOfNumbers = numbers.foldLeft(0) { (total,element) =>
| total + element
| }
sumOfNumbers: Int = 6
foldLeft的第一个参数给出的值0是初始值(这意味着将函数应用于第一个列表元素时,total = 0)。 符号(total,element)表示一个Tuple2,它在Scala中是一个具有2个元素的元组(例如,表示3D空间坐标,通常引用Tuple3(x,y,z)等很有用...)。 请注意,为了求和,Scala API实际上提供了一个“ sum”方法,因此可以写出最后一条语句:
scala> val sumOfNumbers = numbers.sum
sumOfNumbers: Int = 6
您可以从scaladoc API中检查更多的这些集合转换方法。 您也可以链接这些方法(例如,numbers.reverse.filter ...)以获得更简洁的代码,尽管它可能会影响可读性。
最后,以(_ + 10)的形式存在等效于{n => n + 10}的较短符号,这意味着如果您所调用的方法仅暗示了输入参数,则不必声明该输入参数。 在我们的例子中,“ n”被称为匿名变量,因为您可以随意调用它,例如“ x”或“ number”,因此下划线表示您需要用集合中的每个元素填充空格。 (Groovy保留单词“ it”而不是_,Python使用“ self”)。
scala> val higherNumbers = numbers.map(_+10)
higherNumbers: List[Int] = List(11, 12, 13)
在对整数进行基本操作之后,我们准备进入涉及更复杂对象的集合转换,例如使用上面定义的Employee类:
scala> val allEmployees = List(luke,anna,guest,yoda,thomas)
allEmployees: List[Employee] = List(Employee(Luke,30,LucasArt),
Employee(Anna,30,DevCode), Employee(guest,30,DevCode),
Employee(Yoda,800,LucasArt), Employee(Thomas,41,DevCode))
从这5个元素的列表中,我们可以通过应用过滤器来保留DevCode雇员,该过滤器将保留匿名函数为其返回True的雇员:
scala> val devcodeEmployees = allEmployees.filter { _.company == "DevCode" }
devcodeEmployees: List[Employee] = List(Employee(Anna,30,DevCode),
Employee(guest,30,DevCode), Employee(Thomas,41,DevCode))
scala> val oldEmployees = allEmployees.filter(_.age > 100).map(_.name)
oldEmployees: List[String] = List(Yoda)
想象一下,我们拥有的allEmployees集合是从SQL查询获得的结果集,类似于“ SELECT * FROM employee WHERE company ='DevCode'”。 现在,我们可以通过将List [Employee]转换为Map,其中键是公司名称,而值是该公司所有雇员的列表,来按公司对雇员进行排序:
scala> val sortedEmployees = allEmployees.groupBy(_.company)
sortedEmployees: scala.collection.immutable.Map[String,List[Employee]] = Map(DevCode -
> List(Employee(Anna,30,DevCode), Employee(guest,30,DevCode),
Employee(Thomas,41,DevCode)), LucasArt -> List(Employee(Luke,30,LucasArt),
Employee(Yoda,800,LucasArt)))
作为进一步处理存储为该(键-值)哈希表值的每个列表的示例,我们可以想象计算每个公司的员工平均年龄。
实际上,这意味着我们必须对每个列表的每个员工的“年龄”字段求和,然后除以该列表中的员工数量。 让我们首先为DevCode做一下:
scala> devcodeEmployees
res4: List[Employee] = List(Employee(Anna,30,DevCode), Employee(guest,30,DevCode),
Employee(Thomas,41,DevCode))
scala> val devcodeAges = devcodeEmployees.map(_.age)
devcodeAges: List[Int] = List(30, 30, 41)
scala> val devcodeAverageAge = devcodeAges.sum / devcodeAges.size
devcodeAverageAge: Int = 33
回到我们的Map的更一般的情况(键:字符串->值:列表[雇员]),我们现在可以通过写几行来汇总和计算每个公司的平均年龄:
scala> val averageAgeByCompany = sortedEmployees.map{ case(key,value)=>
| value(0).copy(name="average",age=(value.map(_.age).sum)/value.size)}
averageAgeByCompany: scala.collection.immutable.Iterable[Employee] =
List(Employee(average,33,DevCode), Employee(average,415,LucasArt))
“ case(key,value)”是Scala提供的功能非常强大的模式匹配机制的一个示例。 有关更多说明,请参见Scala文档。
现在我们完成了。 我们刚刚写的是一个小的“ Map Reduce”算法。 由于每个公司的员工总数完全独立于其他公司,因此该算法非常易于并行化。
附录中给出了Java和Scala中算法的等效实现。
参考资料
附录
Map Reduce:Java
public class Employee { final String name;
final Integer age;
final String company;
public Employee(String name, Integer age, String company) {
this.name = name == null ? "guest" : name;
this.age = age == null ? 30 : age;
this.company = company == null ? "DevCode" : company;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public String getCompany() {
return company;
}
@Override
public String toString() {
return "Employee [name=" + name + ", age=" + age + ",
company="
+ company + "]";
}
}
class Builder {
String name, company;
Integer age;
Builder(String name) {
this.name = name;
}
Employee build() {
return new Employee(name, age, company);
}
Builder age(Integer age) {
this.age = age;
return this;
}
Builder company(String company) {
this.company = company;
return this;
}
}
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimaps;
public class MapReduce {
public static final void main(String[] args) {
Employee guest = new Builder("Guest").build();
Employee anna = new Builder("Anna").build();
Employee thomas = new Builder("Thomas").age(41).build();
Employee luke = new
Builder("Luke").company("LucasArt").build();
Employee yoda = new
Builder("Yoda").age(800).company("LucasArt").build();
Collection<Employee> employees = new ArrayList<Employee>();
employees.add(guest);
employees.add(anna);
employees.add(thomas);
employees.add(luke);
employees.add(yoda);
ImmutableListMultimap<String, Employee>
personsGroupByCompany = Multimaps.index(employees, new Function<Employee,
String>() {
public String apply(Employee person) {
return person.getCompany();
}
});
for(String company: companyNamesFromMap) {
List<Employee> employeesForThisCompany =
personsGroupByCompany.get(company);
int sum = 0;
for(Employee employee:
employeesForThisCompany) {
sum+= employee.getAge();
}
averageAgeByCompany.add(new
Employee("average",sum/employeesForThisCompany.size(),company));
}
System.out.println("Result: "+averageAgeByCompany);
}
}
MapReduce.scala:
case class Employee(name: String = "guest", age: Int = 30, company: String = "DevCode") val guest = Employee()
val anna = Employee("Anna")
val thomas = Employee("Thomas", 41)
val luke = Employee("Luke", company = "LucasArt")
val yoda = luke.copy("Yoda", age = 800)
val allEmployees = List(luke, anna, guest, yoda, thomas)
val sortedEmployees = allEmployees.groupBy(_.company)
val averageAgeByCompany = sortedEmployees.map { case (key, value) =>
value(0).copy(name = "average", age = (value.map(_.age).sum) / value.size)
}
println("Result: "+averageAgeByCompany)
}
}
java scala