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);
}
