Scala 和 Java 代码的例子,其中 Scala 代码看起来更简单/行更少?

我需要一些 Scala 和 Java 代码的代码样本(我对它们也很好奇) ,这些样本表明 Scala 代码比用 Java 编写的代码更简洁(当然这两个样本应该解决同样的问题)。

如果只有 Scala 样本,并且注释为“这是 Scala 中的抽象工厂,在 Java 中看起来会更加繁琐”,那么这也是可以接受的。

谢谢!

我最喜欢接受和 这个的答案

37717 次浏览

I found this one impressive

Java

public class Person {
private final String firstName;
private final String lastName;
public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
}

Scala

class Person(val firstName: String, val lastName: String)

As well as these ones (sorry for not pasting, I didn't want to steal the code)

Lazily evaluated infinite streams are a good example:

object Main extends Application {


def from(n: Int): Stream[Int] = Stream.cons(n, from(n + 1))


def sieve(s: Stream[Int]): Stream[Int] =
Stream.cons(s.head, sieve(s.tail filter { _ % s.head != 0 }))


def primes = sieve(from(2))


primes take 10 print


}

Here is a question addressing infinite streams in Java: Is an infinite iterator bad design?

Another good example are first class functions and closures:

scala> def f1(w:Double) = (d:Double) => math.sin(d) * w
f1: (w: Double)(Double) => Double


scala> def f2(w:Double, q:Double) = (d:Double) => d * q * w
f2: (w: Double,q: Double)(Double) => Double


scala> val l = List(f1(3.0), f2(4.0, 0.5))
l: List[(Double) => Double] = List(<function1>, <function1>)


scala> l.map(_(2))
res0: List[Double] = List(2.727892280477045, 4.0)

Java doesn't support first class functions, and mimicking closures with anonymous inner classes isn't very elegant. Another thing this example shows that java can't do is running code from an interpreter/REPL. I find this immensely useful for quickly testing code snippets.

Let's improve stacker's example and use Scala's case classes:

case class Person(firstName: String, lastName: String)

The above Scala class contains all features of the below Java class, and some more - for example it supports pattern matching (which Java doesn't have). Scala 2.8 adds named and default arguments, which are used to generate a copy method for case classes, which gives the same ability as the with* methods of the following Java class.

public class Person implements Serializable {
private final String firstName;
private final String lastName;


public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}


public String getFirstName() {
return firstName;
}


public String getLastName() {
return lastName;
}


public Person withFirstName(String firstName) {
return new Person(firstName, lastName);
}


public Person withLastName(String lastName) {
return new Person(firstName, lastName);
}


public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Person person = (Person) o;
if (firstName != null ? !firstName.equals(person.firstName) : person.firstName != null) {
return false;
}
if (lastName != null ? !lastName.equals(person.lastName) : person.lastName != null) {
return false;
}
return true;
}


public int hashCode() {
int result = firstName != null ? firstName.hashCode() : 0;
result = 31 * result + (lastName != null ? lastName.hashCode() : 0);
return result;
}


public String toString() {
return "Person(" + firstName + "," + lastName + ")";
}
}

Then, in usage we have (of course):

Person mr = new Person("Bob", "Dobbelina");
Person miss = new Person("Roberta", "MacSweeney");
Person mrs = miss.withLastName(mr.getLastName());

Against

val mr = Person("Bob", "Dobbelina")
val miss = Person("Roberta", "MacSweeney")
val mrs = miss copy (lastName = mr.lastName)

This is a very simple example: Square integers and then add them


public int sumSquare(int[] list) {
int s = 0;
for(int i = 0; i < list.length; i++) {
s += list[i] * list[i];
}
return s;
}

In scala:


val ar = Array(1,2,3)
def square(x:Int) = x * x
def add(s:Int,i:Int) = s+i


ar.map(square).foldLeft(0)(add)

Compact map applies the function to all elements of the array, so:

Array(1,2,3).map(square)
Array[Int] = Array(1, 4, 9)

Fold left is will start with 0 as the accumulator (s) and apply add(s,i) to all the elements (i) of the array, so that:

 Array(1,4,9).foldLeft(0)(add)  // return 14 form 0 + 1 + 4 + 9

Now this can be further compacted to:

Array(1,2,3).map(x => x * x ).foldLeft(0)((s,i) => s + i )

This one I will not try in Java (to much work), turn XML to a Map:


<a>
<b id="a10">Scala</b>
<b id="b20">rules</b>
</a>

Another one liner to get the map from the XML:


val xml = <a><b id="a10">Scala</b><b id="b20">rules</b></a>


val map = xml.child.map( n => (n \ "@id").text -> n.child.text).toMap
// Just to dump it.
for( (k,v) <- map) println(k + " --> " + v)

How about Quicksort?


Java

The following is a java example found via a google search,

the URL is http://www.mycstutorials.com/articles/sorting/quicksort

public void quickSort(int array[])
// pre: array is full, all elements are non-null integers
// post: the array is sorted in ascending order
{
quickSort(array, 0, array.length - 1);   // quicksort all the elements in the array
}




public void quickSort(int array[], int start, int end)
{
int i = start;      // index of left-to-right scan
int k = end;        // index of right-to-left scan


if (end - start >= 1)               // check that there are at least two elements to sort
{
int pivot = array[start];       // set the pivot as the first element in the partition


while (k > i)                   // while the scan indices from left and right have not met,
{
while (array[i] <= pivot && i <= end && k > i) // from the left, look for the first
i++;                                        // element greater than the pivot
while (array[k] > pivot && k >= start && k >= i) // from the right, look for the first
k--;                                          // element not greater than the pivot
if (k > i)                  // if the left seekindex is still smaller than
swap(array, i, k);      // the right index, swap the corresponding elements
}
swap(array, start, k);          // after the indices have crossed, swap the last element in
// the left partition with the pivot
quickSort(array, start, k - 1); // quicksort the left partition
quickSort(array, k + 1, end);   // quicksort the right partition
}
else // if there is only one element in the partition, do not do any sorting
{
return;                        // the array is sorted, so exit
}
}


public void swap(int array[], int index1, int index2)
// pre: array is full and index1, index2 < array.length
// post: the values at indices 1 and 2 have been swapped
{
int temp      = array[index1];      // store the first value in a temp
array[index1] = array[index2];      // copy the value of the second into the first
array[index2] = temp;               // copy the value of the temp into the second
}

Scala

A quick attempt at a Scala version. Open season for code improvers ;@)

def qsort(l: List[Int]): List[Int] = {
l match {
case Nil         => Nil
case pivot::tail => qsort(tail.filter(_ < pivot)) ::: pivot :: qsort(tail.filter(_ >= pivot))
}
}

Task: Write a program to index a list of keywords (like books).

Explanation:

  • Input: List<String>
  • Output: Map<Character, List<String>>
  • The key of map is 'A' to 'Z'
  • Each list in the map are sorted.

Java:

import java.util.*;


class Main {
public static void main(String[] args) {
List<String> keywords = Arrays.asList("Apple", "Ananas", "Mango", "Banana", "Beer");
Map<Character, List<String>> result = new HashMap<Character, List<String>>();
for(String k : keywords) {
char firstChar = k.charAt(0);
if(!result.containsKey(firstChar)) {
result.put(firstChar, new  ArrayList<String>());
}
result.get(firstChar).add(k);
}
for(List<String> list : result.values()) {
Collections.sort(list);
}
System.out.println(result);
}
}

Scala:

object Main extends App {
val keywords = List("Apple", "Ananas", "Mango", "Banana", "Beer")
val result = keywords.sorted.groupBy(_.head)
println(result)
}

I liked this simple example of sorting and transformation, taken from David Pollak's 'Beginning Scala' book:

In Scala:

def validByAge(in: List[Person]) = in.filter(_.valid).sortBy(_.age).map(_.first)
case class Person(val first: String, val last: String, val age: Int) {def valid: Boolean = age > 18}
validByAge(List(Person("John", "Valid", 32), Person("John", "Invalid", 17), Person("OtherJohn", "Valid", 19)))

In Java:

public static List<String> validByAge(List<Person> in) {
List<Person> people = new ArrayList<Person>();
for (Person p: in) {
if (p.valid()) people.add(p);
}
Collections.sort(people, new Comparator<Person>() {
public int compare(Person a, Person b) {
return a.age() - b.age();
}
} );
List<String> ret = new ArrayList<String>();
for (Person p: people) {
ret.add(p.first);
}
return ret;
}


public class Person {
private final String firstName;
private final String lastName;
private final Integer age;
public Person(String firstName, String lastName, Integer age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
public String getFirst() {
return firstName;
}
public String getLast() {
return lastName;
}
public Integer getAge() {
return age;
}
public Boolean valid() {
return age > 18;
}
}


List<Person> input = new ArrayList<Person>();
input.add(new Person("John", "Valid", 32));
input.add(new Person("John", "InValid", 17));
input.add(new Person("OtherJohn", "Valid", 19));


List<Person> output = validByAge(input)

Problem: you need to design a method that will execute any given code asynchronously.

Solution in Java:

/**
* This method fires runnables asynchronously
*/
void execAsync(Runnable runnable){
Executor executor = new Executor() {
public void execute(Runnable r) {
new Thread(r).start();
}
};
executor.execute(runnable);
}


...


execAsync(new Runnable() {
public void run() {
...   // put here the code, that need to be executed asynchronously
}
});

The same thing in Scala (using actors):

def execAsync(body: => Unit): Unit = {
case object ExecAsync
actor {
start; this ! ExecAsync
loop {
react {
case ExecAsync => body; stop
}
}
}
}


...


execAsync{  // expressive syntax - don't need to create anonymous classes
...  // put here the code, that need to be executed asynchronously
}

This Scala code...

def partition[T](items: List[T], p: (T, T) => Boolean): List[List[T]] = {
items.foldRight[List[List[T]]](Nil)((item: T, items: List[List[T]]) => items match {
case (first :: rest) :: last if p (first, item) =>
(List(item)) :: (first :: rest) :: last
case (first :: rest) :: last =>
(item :: first :: rest) :: last
case _ => List(List(item))
})
}

...would be completely unreadable in Java, if possible at all.

Why nobody posted this before:

Java:

class Hello {
public static void main( String [] args ) {
System.out.println("Hello world");
}
}

116 characters.

Scala:

object Hello extends App {
println("Hello world")
}

56 characters.

A map of actions to perform depending on a string.

Java 7:

// strategy pattern = syntactic cruft resulting from lack of closures
public interface Todo {
public void perform();
}


final Map<String, Todo> todos = new HashMap<String,Todo>();
todos.put("hi", new Todo() {
public void perform() {
System.out.println("Good morning!");
}
} );


final Todo todo = todos.get("hi");
if (todo != null)
todo.perform();
else
System.out.println("task not found");

Scala:

val todos = Map( "hi" -> { () => println("Good morning!") } )
val defaultFun = () => println("task not found")
todos.getOrElse("hi", defaultFun).apply()

And it's all done in the best possible taste!

Java 8:

Map<String, Runnable> todos = new HashMap<>();
todos.put("hi", () -> System.out.println("Good morning!"));
Runnable defaultFun = () -> System.out.println("task not found");
todos.getOrDefault("hi", defaultFun).run();

Task:

You have got an XML file "company.xml" that looks like this:

<?xml version="1.0"?>
<company>
<employee>
<firstname>Tom</firstname>
<lastname>Cruise</lastname>
</employee>
<employee>
<firstname>Paul</firstname>
<lastname>Enderson</lastname>
</employee>
<employee>
<firstname>George</firstname>
<lastname>Bush</lastname>
</employee>
</company>

You have to read this file and print the firstName and lastName fields of all the employees.


Java: [ taken from here ]

import java.io.File;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;


public class XmlReader {
public static void main(String[] args) {
try {
File file = new File("company.xml");
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(file);
doc.getDocumentElement().normalize();
NodeList nodeLst = doc.getElementsByTagName("employee");
for (int s = 0; s < nodeLst.getLength(); s++) {
Node fstNode = nodeLst.item(s);
if (fstNode.getNodeType() == Node.ELEMENT_NODE) {
Element fstElmnt = (Element) fstNode;
NodeList fstNmElmntLst = fstElmnt.getElementsByTagName("firstname");
Element fstNmElmnt = (Element) fstNmElmntLst.item(0);
NodeList fstNm = fstNmElmnt.getChildNodes();
System.out.println("First Name: "  + ((Node) fstNm.item(0)).getNodeValue());
NodeList lstNmElmntLst = fstElmnt.getElementsByTagName("lastname");
Element lstNmElmnt = (Element) lstNmElmntLst.item(0);
NodeList lstNm = lstNmElmnt.getChildNodes();
System.out.println("Last Name: " + ((Node) lstNm.item(0)).getNodeValue());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}



Scala: [ taken from here, slide #19 ]

import xml.XML


object XmlReader {
def main(args: Array[String]): Unit = {
XML.loadFile("company.xml") match {
case <employee> { employees @ _* } </employee> => {
for(e <- employees) {
println("First Name: " + (e \ "firstname").text)
println("Last Name: " + (e \ "lastname").text)
}
}
}
}
}

[ EDIT by Bill; Check comments for the discussion ] --

Hmm, how to do it without replying in an unformatted reply section... Hmph. I guess I'll edit your answer and let you delete it if it bugs you.

This is how I would do it in Java with better libraries:

public scanForEmployees(String filename) {
GoodXMLLib source=new GoodXMLLib(filename);
while( String[] employee: source.scanFor("employee", "firstname", "lastname") )
{
System.out.println("First Name: " + employee[0]);
System.out.println("Last Name: " + employee[1]);
}
}

This is just a quick hack involving no magic and all reusable components. If I wanted to add some magic I could do something better than returning an array of string arrays, but even as is this GoodXMLLib would be completely reusable. The first parameter of scanFor is the section, all future parameters would be the items to find which is limited, but the interface could be buffed slightly to add multiple levels of matching with no real problem.

I will admit that Java has some pretty poor library support in general, but come on--to compare a horrible usage of Java's decade(?) old XML library to an implementation done based on being terse is just not fair--and is far from a comparison of the languages!

The Circuit Breaker pattern from Michael Nygard's Release It in FaKods (link to code)

implementation looks like this in Scala:

. . .
addCircuitBreaker("test", CircuitBreakerConfiguration(100,10))
. . .




class Test extends UsingCircuitBreaker {
def myMethodWorkingFine = {
withCircuitBreaker("test") {
. . .
}
}


def myMethodDoingWrong = {
withCircuitBreaker("test") {
require(false,"FUBAR!!!")
}
}
}

Which I think is super nice. It looks just as a pice of the language but it is a simple mixin in the CircuitBreaker Object doing all work.

/**
* Basic MixIn for using CircuitBreaker Scope method
*
* @author Christopher Schmidt
*/
trait UsingCircuitBreaker {
def withCircuitBreaker[T](name: String)(f: => T): T = {
CircuitBreaker(name).invoke(f)
}
}

Reference in other languages Google for "Circuit breaker" + your language.

I like much the method getOrElseUpdate, found in mutableMap and shown here, first Java, without:

public static Map <String, Integer> wordCount (Scanner sc, String delimiters) {
Map <String, Integer> dict = new HashMap <String, Integer> ();
while (sc.hasNextLine ()) {
String[] words = sc.nextLine ().split (delimiters);
for (String word: words) {
if (dict.containsKey (word)) {
int count = dict.get (word);
dict.put (word, count + 1);
} else
dict.put (word, 1);
}
}
return dict;
}

yes - a WordCount, and here in scala:

def wordCount (sc: Scanner, delimiter: String) = {
val dict = new scala.collection.mutable.HashMap [String, Int]()
while (sc.hasNextLine ()) {
val words = sc.nextLine.split (delimiter)
words.foreach (word =>
dict.update (word, dict.getOrElseUpdate (word, 0) + 1))
}
dict
}

And here it is in Java 8:

public static Map<String, Integer> wordCount(Scanner sc, String delimiters)
{
Map<String, Integer> dict = new HashMap<>();
while (sc.hasNextLine())
{
String[] words = sc.nextLine().split(delimiters);
Stream.of(words).forEach(word -> dict.merge(word, 1, Integer::sum));
}
return dict;
}

And if you want to go 100% functional:

import static java.util.function.Function.identity;
import static java.util.stream.Collectors.*;


public static Map<String, Long> wordCount(Scanner sc, String delimiters)
{
Stream<String> stream = stream(sc.useDelimiter(delimiters));
return stream.collect(groupingBy(identity(), counting()));
}


public static <T> Stream<T> stream(Iterator<T> iterator)
{
Spliterator<T> spliterator = Spliterators.spliteratorUnknownSize(iterator, 0);
return StreamSupport.stream(spliterator, false);
}

filter and sort have already been shown, but look how easy they are integrated with the map:

    def filterKeywords (sc: Scanner, keywords: List[String]) = {
val dict = wordCount (sc, "[^A-Za-z]")
dict.filter (e => keywords.contains (e._1)).toList . sort (_._2 < _._2)
}

I liked user unknown's answer so much I'm going to try to improve upon it. The code below is not a direct translation of the Java example, but it accomplishes the same task with the same API.

def wordCount (sc: Scanner, delimiter: String) = {
val it = new Iterator[String] {
def next = sc.nextLine()
def hasNext = sc.hasNextLine()
}
val words = it flatMap (_ split delimiter iterator)
words.toTraversable groupBy identity mapValues (_.size)
}

Task:

You have a list people of objects of class Person that has fields name and age. Your task is to sort this list first by name, and then by age.

Java 7:

Collections.sort(people, new Comparator<Person>() {
public int compare(Person a, Person b) {
return a.getName().compare(b.getName());
}
});
Collections.sort(people, new Comparator<Person>() {
public int compare(Person a, Person b) {
return Integer.valueOf(a.getAge()).compare(b.getAge());
}
});

Scala:

val sortedPeople = people.sortBy(p => (p.name, p.age))

Update

Since I wrote this answer, there has been quite some progress. The lambdas (and method references)have finally landed in Java, and they are taking the Java world by storm.

This is what the above code will look like with Java 8 (contributed by @fredoverflow):

people.sort(Comparator.comparing(Person::getName).thenComparing(Person::getAge));

While this code is almost as short, it does not work quite as elegantly as the Scala one.

In Scala solution, the Seq[A]#sortBy method accepts a function A => B where B is required to A => B1 an Ordering. Ordering is a type-class. Think best of both worlds: Like Comparable, it's implicit for the type in question, but like Comparator, it's extensible and can be added retrospectively to types that did not have it. Since Java lacks type-classes, it has to duplicate every such method, once for Comparable, then for Comparator. For example, see comparing and A => B0 A => B2.

The type-classes allow one to write rules such as "If A has ordering and B has ordering, then their tuple (A, B) also has ordering". In code, that is:

implicit def pairOrdering[A : Ordering, B : Ordering]: Ordering[(A, B)] = // impl

That is how the sortBy in our code can compare by name and then by age. Those semantics will be encoded with the above "rule". A Scala programmer would intuitively expect this to work this way. No special purpose methods like comparing had to be added to Ordering.

Lambdas and method references are just a tip of an iceberg that is functional programming. :)

I'm writing a Blackjack game in Scala now. Here is how my dealerWins method would look in Java:

boolean dealerWins() {
for(Player player : players)
if (player.beats(dealer))
return false;
return true;
}

Here's how it looks in Scala:

def dealerWins = !(players.exists(_.beats(dealer)))

Hooray for higher-order functions!

Java 8 solution:

boolean dealerWins() {
return players.stream().noneMatch(player -> player.beats(dealer));
}

I am preparing a document that gives several examples of Java and Scala code, utilising only the simple to understand features of Scala:

Scala : A better Java

If you would like me to add something to it, please reply in the comments.