Stream官方库与第三方库常见操作对比
0. 写在前面
三种库对比分别为:
- java8官方stream
- streamex
- joolambda
本文章对常见开发中的一些对stream的使用进行比对,并进行个人评价。Ps:仅为个人评价。
1. 常见姿势
主要结合平时日常使用的一些场景进行对比。
1. 构造
@Test
public void testConstruct(){
ArrayList<Person> people = Lists.newArrayList(new Person(1, "John", "Smith"),
new Person(2, "Tom", "Hamilton"),
new Person(3, "Paul", "Walker"));
// java8
Stream.of(people);
people.stream();
// streamEx
StreamEx.of(people);
// joolambda
Seq.seq(people);
}
2. 提取属性
@Test
public void testExtractProperty(){
ArrayList<Person> people = Lists.newArrayList(new Person(1, "John", "Smith"),
new Person(2, "Tom", "Hamilton"),
new Person(3, "Paul", "Walker"));
// java8 略显繁琐,尚可接受
List<Integer> idList1 = people.stream().map(Person::getId).collect(Collectors.toList());
// streamEx
List<Integer> idList2 = StreamEx.of(people).map(Person::getId).toList();
// joolambda
List<Integer> idList3 = Seq.seq(people).map(Person::getId).toList();
}
3. 去重复
@Test
public void testCustomDistinct(){
ArrayList<Person> people = Lists.newArrayList(new Person(1, "John", "Smith"),
new Person(2, "Tom", "Hamilton"),
new Person(2, "Tom", "Hamilton"),
new Person(2, "Tom", "Hamilton"),
new Person(3, "Paul", "Walker"),
new Person(3, "Paul", "Walker"));
// 按照id去重
// java8 巨麻烦,这也是为什么要用其他库的原因。
Set<Object> seen = Sets.newHashSet();
Function<Person, Object> keyExtractor = Person::getId;
Predicate<Person> distinct = p -> seen.add(keyExtractor.apply(p));
List<Person> distinctList1 = people.stream().filter(distinct).collect(Collectors.toList());
System.out.println(distinctList1);
List<Integer> idList1 = distinctList1.stream().map(Person::getId).collect(Collectors.toList());
Assertions.assertThat(idList1).doesNotHaveDuplicates();
// streamEx
List<Person> distinctList2 = StreamEx.of(people).distinct(Person::getId).toList();
List<Integer> idList2 = StreamEx.of(distinctList2).map(Person::getId).toList();
Assertions.assertThat(idList2).doesNotHaveDuplicates();
// joolambda
List<Person> distinctList3 = Seq.seq(people).distinct(Person::getId).toList();
List<Integer> idList3 = Seq.seq(distinctList3).map(Person::getId).toList();
Assertions.assertThat(idList3).doesNotHaveDuplicates();
// streamEx和joolambda都好用
}
3.1 按照多个Key去重
@Test
public void testCustomDistinctByMutliKey(){
ArrayList<Person> people = Lists.newArrayList(new Person(1, "John", "Smith"),
new Person(2, "Tom", "Hamilton"),
new Person(2, "Tom1", "Hamilton"),
new Person(2, "Tom2", "Hamilton"),
new Person(3, "Paul", "Walker"),
new Person(3, "Paul1", "Walker"));
// 按照id去重
// java8
Set<Object> seen = Sets.newHashSet();
Function<Person, Object> keyExtractor = person -> person.getId() + "#" + person.getFirstName();
Predicate<Person> distinct = p -> seen.add(keyExtractor.apply(p));
List<Person> distinctList1 = people.stream().filter(distinct).collect(Collectors.toList());
System.out.println(distinctList1);
Assertions.assertThat(distinctList1.size()).isEqualTo(6);
// streamEx
List<Person> distinctList2 = StreamEx.of(people).distinct(person -> person.getId() + "#" + person.getFirstName()).toList();
Assertions.assertThat(distinctList2.size()).isEqualTo(6);
// joolambda
List<Person> distinctList3 = Seq.seq(people).distinct(person -> person.getId() + "#" + person.getFirstName()).toList();
Assertions.assertThat(distinctList3.size()).isEqualTo(6);
}
4. List2Map
List2Map有个需要处理的地方是,如果出现重复Key,会抛异常。所以一般会通过distinct过滤下。 这里example就先按照过滤后的样子来处理。
@Test
public void testList2Map(){
ArrayList<Person> people = Lists.newArrayList(new Person(1, "John", "Smith"),
new Person(2, "Tom", "Hamilton"),
new Person(2, "Tom", "Hamilton"),
new Person(2, "Tom", "Hamilton"),
new Person(3, "Paul", "Walker"),
new Person(3, "Paul", "Walker"));
List<Person> unqiuePeople = StreamEx.of(people).distinct(Person::getId).toList();
List<Integer> unqiueIds = unqiuePeople.stream().map(Person::getId).collect(Collectors.toList());
// java8
Map<Integer, Person> map1 = unqiuePeople.stream().collect(Collectors.toMap(Person::getId, t->t));
Set<Integer> set1 = map1.keySet();
Assertions.assertThat(set1).containsAll(unqiueIds);
// streamEx
Map<Integer, Person> map2 = StreamEx.of(unqiuePeople).toMap(Person::getId, t->t);
Set<Integer> set2 = map2.keySet();
Assertions.assertThat(set2).containsAll(unqiueIds);
// joolambda
Map<Integer, Person> map3 = Seq.seq(unqiuePeople).toMap(Person::getId);
Set<Integer> set3 = map3.keySet();
Assertions.assertThat(set3).containsAll(unqiueIds);
// 三种方式都可接受
}
5. groupBy
groupBy 算是比较繁琐的了。算是日常开发的痛点之一了。
@Test
public void testGroupBy(){
ArrayList<Person> people = Lists.newArrayList(new Person(1, "John", "Smith"),
new Person(2, "Tom", "Hamilton"),
new Person(2, "Tom1", "Hamilton"),
new Person(2, "Tom2", "Hamilton"),
new Person(3, "Paul", "Walker"),
new Person(3, "Paul1", "Walker"));
// java8
Map<Integer, List<Person>> group1 = people.stream().collect(Collectors.groupingBy(Person::getId));
// streamEx
Map<Integer, List<Person>> group2 = StreamEx.of(people).groupingBy(Person::getId);
// joolambda
Map<Integer, List<Person>> group3 = Seq.seq(people).groupBy(Person::getId);
Assertions.assertThat(group1).containsAllEntriesOf(group2);
Assertions.assertThat(group2).containsAllEntriesOf(group3);
}
5.1 groupBy生成unqiueList
这算是我觉得能被stream解决的最好的问题的示范了。
@Test
public void testGroupByAndDistinct(){
ArrayList<Person> people = Lists.newArrayList(new Person(1, "John", "Smith"),
new Person(2, "Tom", "Hamilton"),
new Person(2, "Tom1", "Hamilton"),
new Person(2, "Tom1", "Hamilton"),
new Person(2, "Tom2", "Hamilton"),
new Person(3, "Paul", "Walker"),
new Person(3, "Paul", "Walker"),
new Person(3, "Paul1", "Walker"));
// java8
Function<List<Person>, List<Person>> disctinctById = people1 -> StreamEx.of(people1).distinct(Person::getFirstName).toList();
Map<Integer, List<Person>> group1 = people.stream().collect(Collectors.groupingBy(Person::getId, Collectors.collectingAndThen(
Collectors.toList(),
disctinctById
)));
List<String> group1NameList = group1.get(2).stream().map(Person::getFirstName).collect(Collectors.toList());
Assertions.assertThat(group1NameList).doesNotHaveDuplicates();
// streamEx
Map<Integer, List<Person>> group2 =
StreamEx.of(people).groupingBy(Person::getId, Collectors.collectingAndThen(
Collectors.toList(),
disctinctById));
List<String> group2NameList = group2.get(2).stream().map(Person::getFirstName).collect(Collectors.toList());
Assertions.assertThat(group2NameList).doesNotHaveDuplicates();
// joolambda
Map<Integer, List<Person>> group3 =
Seq.seq(people).groupBy(Person::getId, Collectors.collectingAndThen(
Collectors.toList(),
disctinctById));
List<String> group3NameList = group3.get(2).stream().map(Person::getFirstName).collect(Collectors.toList());
Assertions.assertThat(group3NameList).doesNotHaveDuplicates();
// 因为StreamEx或者Seq都是继承了stream类,所以收集操作都是可以一体的。
}
6. select
6.1 selectFirst
@Test
public void testSelectFirst(){
Person first = new Person(1, "John", "Smith");
ArrayList<Person> people = Lists.newArrayList(first,
new Person(2, "Tom", "Hamilton"));
// 日常开发中,我们平时都会使用批量接口,有时候可能是没有单个接口的。
// 所以批量接口也会被我们当作单个接口使用。这时候selectFirst就很有用了。
// java8
Person person1 = people.stream().findFirst().orElseThrow(NoSuchElementException::new);
Assertions.assertThat(person1.getId()).isEqualTo(1);
// streamEx
Person person2 = StreamEx.of(people).findFirst().orElseThrow(NoClassDefFoundError::new);
Assertions.assertThat(person2.getId()).isEqualTo(1);
// joolambda
Person person3 = Seq.seq(people).findFirst().orElseThrow(NoClassDefFoundError::new);
Assertions.assertThat(person3.getId()).isEqualTo(1);
}
6.2 selectByCondition
@Test
public void testSelectByCondition(){
Person first = new Person(1, "John", "Smith");
Person second = new Person(2, "John", "Smith");
ArrayList<Person> people = Lists.newArrayList(first, second);
// java8
Person person1 = people.stream().filter(person -> person.getId() == 2).findAny().orElseThrow(NoSuchElementException::new);
Assertions.assertThat(person1).isEqualTo(second);
// streamEx
Person person2 = StreamEx.of(people).findAny(person -> person.getId()==2).orElseThrow(NoClassDefFoundError::new);
Assertions.assertThat(person2).isEqualTo(second);
// joolambda
Person person3 = Seq.seq(people).filter(person->person.getId()==2).findAny().orElseThrow(NoClassDefFoundError::new);
Assertions.assertThat(person3).isEqualTo(second);
}
7 过滤Map中的key
@Test
public void testRemoveKeyOfMap(){
ArrayList<Person> people = Lists.newArrayList(new Person(1, "John", "Smith"),
new Person(2, "Tom", "Hamilton"),
new Person(3, "Paul", "Walker"));
Map<Integer, Person> map1 = Seq.seq(people).toMap(Person::getId);
Map<Integer, Person> map2 = Seq.seq(people).toMap(Person::getId);
Map<Integer, Person> map3 = Seq.seq(people).toMap(Person::getId);
// java8
// streamEx
Map<Integer, Person> removeMap2 = EntryStream.of(map2).removeKeys(id -> id == 2).toMap();
Assertions.assertThat(removeMap2.get(2)).isNull();
// joolambda
Predicate<Map.Entry<Integer, Person>> filterEq2 = entry -> entry.getKey() == 2;
Map<Integer, Person> removeMap3 = Seq.seq(map3.entrySet()).filter(filterEq2.negate()).toMap(entry -> entry.getKey(), entry -> entry.getValue());
Assertions.assertThat(removeMap3.get(2)).isNull();
}
结论
总结在最后,感觉StreamEx和joolambda都很不错。但是StreamEx的接口叫法更加贴近原生的stream,所以倾向于使用StreamEx。