Functional Programming in Java
Lambda expressions are lightweight, highly concise anonymous methods backed by functional interfaces in Java 8. You can use them to leap forward into a whole new world of programming in Java.
优点:
-
声明式
-
提倡不可变性
-
避免副作用
-
优先使用表达式而不是语句
-
使用高阶函数进行设计
Usage of collection
对于列表
static final List<String> stringList = Arrays.asList("hello", "world", "hello", "lambda");
传统的打印方法如下:
@Test
public void commonTest() {
for(String string : stringList) {
System.out.println(string);
}
}
引入新的方法如下:
@Test
public void oneTest() {
stringList.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
}
@Test
public void twoTest() {
stringList.forEach((final String s) -> System.out.println(s));
}
@Test
public void threeTest() {
stringList.forEach((s) -> System.out.println(s));
}
@Test
public void fourTest() {
stringList.forEach(System.out::println);
}
List translate
想将上述列表全部转化为大写。
- common way
@Test
public void commonTest() {
List<String> strings = new LinkedList<>();
for (String string : stringList) {
strings.add(string.toUpperCase());
}
System.out.println(strings);
}
- new way
@Test
public void innerIterTest() {
List<String> strings = new LinkedList<>();
stringList.forEach(s -> strings.add(s.toUpperCase()));
System.out.println(strings);
}
@Test
public void streamTest() {
stringList
.stream()
.map(s -> s.toUpperCase())
.forEach(s -> System.out.println(s));
}
@Test
public void methodReferenceTest() {
stringList
.stream()
.map(String::toUpperCase)
.forEach(System.out::println);
}
Collection filter
@Test
public void filterTest() {
stringList
.stream()
.filter(s->s.startsWith("g"))
.forEach(System.out::println);
}
一、复用 lambda
static final List<String> colorList = Arrays.asList("red", "green", "hack", "yellow");
@Test
public void reuseLambdaTest() {
final Predicate<String> startsWithG = name -> name.startsWith("g");
stringList.stream().filter(startsWithG).forEach(System.out::println);
colorList.stream().filter(startsWithG).forEach(System.out::println);
}
这里复用的依然不够彻底。
二、场景2
查找以 g/w 开头的字符串
/**
* 查找以 g/w 开头的字符串
*/
@Test
public void filterTest() {
final Predicate<String> startsWithG = name -> name.startsWith("g");
final Predicate<String> startsWithW = name -> name.startsWith("w");
stringList
.stream()
.filter(startsWithG)
.forEach(System.out::println);
stringList
.stream()
.filter(startsWithW)
.forEach(System.out::println);
}
1、存在冗余。可以使用静态方法消除。
/**
* 判断是否以某字符开始
*
* @param letter
* @return
*/
private static Predicate<String> checkIfStartsWith(final String letter) {
return name -> name.startsWith(letter);
}
@Test
public void staticMethodTest() {
stringList
.stream()
.filter(checkIfStartsWith("g"))
.forEach(System.out::println);
stringList
.stream()
.filter(checkIfStartsWith("w"))
.forEach(System.out::println);
}
2、使用函数,替换静态方法
@Test
public void functionTest() {
final Function<String, Predicate<String>> startsWithLetter = (String letter) -> {
Predicate<String> checkStarts = (String name) -> name.startsWith(letter);
return checkStarts;
};
stringList
.stream()
.filter(startsWithLetter.apply("g"))
.forEach(System.out::println);
stringList
.stream()
.filter(startsWithLetter.apply("w"))
.forEach(System.out::println);
}
上述方法可以使用 lambda 简化如下:
@Test
public void lambdaFuncTest() {
final Function<String, Predicate<String>> startsWithLetter =
(String letter) -> (String name) -> name.startsWith(letter);
//...
}
Optional
/**
* 查询符合条件的字符串内容
* @param names
* @param startingLetter
*/
private static void pickName(
final List<String> names, final String startingLetter) {
final Optional<String> foundName =
names.stream()
.filter(name ->name.startsWith(startingLetter))
.findFirst();
System.out.println(String.format("A name starting with %s: %s",
startingLetter, foundName.orElse("No name found")));
}
测试如下:
@Test
public void optionalTest() {
pickName(stringList, "h");
pickName(stringList, "Z");
}
result:
A name starting with h: hello
A name starting with Z: No name found
MapReduce
/**
* 计算全部字符串的长度之和
*/
@Test
public void sumTest() {
int total = stringList.stream().mapToInt(s->s.length()).sum();
System.out.println(total);
}
/**
* 找到最长的一个字符串
*/
@Test
public void longestTest() {
final Optional<String> aLongName = stringList.stream()
.reduce((name1, name2) ->
name1.length() >= name2.length() ? name1 : name2);
aLongName.ifPresent(name ->
System.out.println(String.format("A longest name: %s", name)));
}
/**
* 列表内容进行拼接
*/
@Test
public void joinTest() {
String string = stringList.stream()
.map(String::toUpperCase)
.collect(Collectors.joining(","));
System.out.println(string);
}
String Iterator
// 104
// 101
// 108
// 108
// 111
@Test
public void commonTest() {
final String string = "hello";
string.chars().forEach(System.out::println);
}
// h
// e
// l
// l
// o
@Test
public void toCharTest() {
final String string = "hello";
string.chars()
.mapToObj(ch-> ((char) ch))
.forEach(System.out::println);
}
Collect
- Person.java
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
- CollectTest.java
/**
* 构建列表
* @return
*/
private static List<Person> buildPersonList() {
List<Person> personList = new LinkedList<>();
personList.add(new Person("Apple", 12));
personList.add(new Person("Box", 12));
personList.add(new Person("Cat", 20));
personList.add(new Person("Dog", 26));
personList.add(new Person("Eye", 62));
return personList;
}
//[Person{name='D', age=26}, Person{name='E', age=62}]
@Test
public void older20Test() {
List<Person> older20List = new LinkedList<>();
buildPersonList().stream().filter(person -> person.getAge() > 20)
.forEach(person -> older20List.add(person));
System.out.println(older20List);
}
//[Person{name='D', age=26}, Person{name='E', age=62}]
@Test
public void older20CollectTest() {
List<Person> older20List = buildPersonList().stream()
.filter(person -> person.getAge() > 20)
.collect(Collectors.toList());
System.out.println(older20List);
}
// Grouped by age: {20=[Person{name='C', age=20}], 26=[Person{name='D', age=26}], 12=[Person{name='A', age=12}, Person{name='B', age=12}], 62=[Person{name='E', age=62}]}
@Test
public void groupByAgeTest() {
Map<Integer, List<Person>> peopleByAge =
buildPersonList().stream()
.collect(Collectors.groupingBy(Person::getAge));
System.out.println("Grouped by age: " + peopleByAge);
}
// People grouped by age: {20=[C], 26=[D], 12=[A, B], 62=[E]}
@Test
public void getNameGroupByAgeTest() {
Map<Integer, List<String>> nameOfPeopleByAge =
buildPersonList().stream()
.collect(Collectors.groupingBy(Person::getAge, Collectors.mapping(Person::getName, Collectors.toList())));
System.out.println("People grouped by age: " + nameOfPeopleByAge);
}
// Oldest person of each letter:
// {A=Optional[Person{name='Apple', age=12}], B=Optional[Person{name='Box', age=12}], C=Optional[Person{name='Cat', age=20}], D=Optional[Person{name='Dog', age=26}], E=Optional[Person{name='Eye', age=62}]}
@Test
public void groupByFirstLetterTest() {
Comparator<Person> byAge = Comparator.comparing(Person::getAge);
Map<Character, Optional<Person>> oldestPersonOfEachLetter =
buildPersonList().stream()
.collect(Collectors.groupingBy(person -> person.getName().charAt(0),
Collectors.reducing(BinaryOperator.maxBy(byAge))));
System.out.println("Oldest person of each letter:");
System.out.println(oldestPersonOfEachLetter);
}