今天和大家聊聊Java8的流特性。
1、java8的流能做什么?
(1)filter:从集合中筛选出符合条件的数据。
(2)distinct:对集合中重复的元素去重。
(3)collect:对集合进行的一系列关系映射。
(4)map:获取集合中某类数据的集合
使用过Java8流特性的伙伴们都知道,它在很大程度上简化了我们的代码。
那么,上面提到的作用,具体怎么实现呢?
2、具体使用方式:
2.1. 创建一个类Person,如下:
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Objects;
/**
* @Author: Max
* @Date: 2021/8/3
* @Function: 实体类-人
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
// 编号
private String code;
// 姓名
private String name;
// 生日
private Date birthday;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return Objects.equals(code, person.code) &&
Objects.equals(name, person.name) &&
Objects.equals(birthday, person.birthday);
}
@Override
public int hashCode() {
return Objects.hash(code, name, birthday);
}
}
2.2. 具体使用方式:
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.Date;
/**
* @Author: Max
* @Date: 2021/8/3
* @Function: 流特性使用测试
*/
public class Test {
/**
*@author: Max
*@time: 2021/8/3
*@description: 测试主方法
*@param: [args]
*@return: void
*/
public static void main(String[] args) {
// 测试流特性
testStream();
}
/**
*@author: Max
*@time: 2021/8/3
*@description: 测试流特性
*@param: []
*@return: void
*/
private static void testStream() {
// 造点数据
List<Person> personList = getPersons();
// Person对象中的属性间映射 id -> name
Map<String, String> idToNameMap = personList.stream().collect(Collectors.toMap(Person::getCode, Person::getName));
System.out.println(idToNameMap);
// Person对象中的某个属性映射对象 id -> Person
Map<String, Person> idToPersonMap = personList.stream().collect(Collectors.toMap(Person::getCode, p -> p, (k1, k2) -> k1));
System.out.println(idToPersonMap);
// 获取Person对象中某个属性的集合
List<String> nameList = personList.stream().map(Person::getName).collect(Collectors.toList());
System.out.println(nameList);
// 过滤掉name属性的值不为A2的数据、筛选出name属性的值为A2的数据
List<Person> filterNameList = personList.stream().filter(x -> "A2".equals(x.getName())).collect(Collectors.toList());
System.out.println(filterNameList);
// Person对象中的属性映射对象的集合 id -> List<Person>
Map<String, List<Person>> idToListMap = personList.stream().collect(Collectors.groupingBy(Person::getCode));
System.out.println(idToListMap);
// Person对象中的多个属性映射对象的集合 id + name -> List<Person>
Map<String, List<Person>> strToListMap = personList.stream().collect(Collectors.groupingBy(p -> p.getCode() + "#" + p.getName()));
for (Map.Entry<String, List<Person>> stringListEntry : strToListMap.entrySet()) {
if (stringListEntry.getValue().size() >= 2) {
//截取"#"之前的字符串
String beforeStr = stringListEntry.getKey().substring(0, stringListEntry.getKey().indexOf("#"));
System.out.println(beforeStr);
//截取"#"之后的字符串
String afterStr = stringListEntry.getKey().substring(beforeStr.length() + 1);
System.out.println(afterStr);
}
}
System.out.println(strToListMap);
// Person对象中的多个属性映射对象 id+name -> Person
//eg1:
Map idAndNameToPersonMap1 = personList.stream().collect(Collectors.toMap(p -> p.getCode() + p.getName(), Function.identity()));
System.out.println(idAndNameToPersonMap1);
//eg1:
Map idAndNameToPersonMap2 = personList.stream().collect(Collectors.toMap(p -> p.getCode() + p.getName(), ClassName -> ClassName));
System.out.println(idAndNameToPersonMap2);
List<Person> listSortedByBirthday = personList.stream().sorted(Comparator.comparing(Person::getBirthday)).collect(Collectors.toList());
}
/**
*@author: Max
*@time: 2021/8/3
*@description: 造数
*@param: []
*@return: java.util.List<com.ymt.foundcore.base.domain.dto.Person>
*/
private static List<Person> getPersons() {
List<Person> personList = new ArrayList<>();
Person person1 = Person.builder()
.code("1")
.name("A1")
.birthday(new Date());
.build();
personList.add(person1);
Person person2 = Person.builder()
.code("2")
.name("A2")
.birthday(new Date());
.build();
personList.add(person2);
Person person3 = Person.builder()
.code("3")
.name("A3")
.birthday(new Date());
.build();
personList.add(person3);
Person person4 = Person.builder()
.code("4")
.name("A4")
.birthday(new Date());
.build();
personList.add(person4);
return personList;
}
}
运行结果如下:
{1=A1, 2=A2, 3=A3, 4=A4}
{1=Person(code=1, name=A1), 2=Person(code=2, name=A2), 3=Person(code=3, name=A3), 4=Person(code=4, name=A4)}
[A1, A2, A3, A4]
[Person(code=2, name=A2)]
{1=[Person(code=1, name=A1)], 2=[Person(code=2, name=A2)], 3=[Person(code=3, name=A3)], 4=[Person(code=4, name=A4)]}
{1#A1=[Person(code=1, name=A1)], 3#A3=[Person(code=3, name=A3)], 2#A2=[Person(code=2, name=A2)], 4#A4=[Person(code=4, name=A4)]}
我们可以看到,流操作的对象是集合。使用流时注意重写equals()和hashCode()方法。使用stream流的distinct()方法去重时,如果没有重写这两个方法,会调用Object类的equals()和hashCode()方法。
注意:例子”Person对象中的多个属性映射对象 id+name -> Person“ 由于是后面添加的笔记,所以没有输出结果。
有些博客说:
stream流的distinct去重的底层使用的hashSet去重,而hashSet实际上是HashMap,所以distinct去重实际上是用了HashMap的key去重的方法,因为hashMap在put元素的时候的代码是先使用hashCode(key)方法得到这个key的hashCode值,然后通过indexFor()方法对hashCode值和table.length数组长度做与运算进而确定要添加的元素放在哪个桶中,然后for循环拿到桶的hashCode值(for循环是因为桶下可能有长度为8的链表,1.8之后还可能有红黑树)来判断这个元素的hashCode值是否和已经存在的桶中元素的hashCode值相等,如果hashCode值相等的话,再去使用equals()方法判断这个值是否和已经存在的桶中的值相等,如果想等,那么原来的key对应的value就会被新的key对应的value代替掉,所以Stream.distinct()去重也要去重写实体类的hashCOde()和distinct()方法。