lambda表达式提供了很多的集合类和收集器来简化编程,使之更加方便和美观,所以这里介绍一些常用的集合类和收集器来处理繁杂的代码。
github的demo地址: https://github.com/BradenLei/lambda
1、方法引用:形如 User::getName,TreeSet::new 等价于user.getName(), new TreeSet<>();
可以引用静态方法、实例对象方法、构造方法等。
/**
* 方法引用
*/
@Test
public void refTest() {
List collect = list.stream().map(u -> u.getName()).collect(Collectors.toList());
System.out.println(collect);
// 等价于 User::getName
System.out.println(list.stream().map(User::getName).collect(Collectors.toList()));
// TreeSet::new
Stream integerStream = Stream.of(1, 2, 3);
TreeSet integerTreeSet = integerStream.collect(Collectors.toCollection(TreeSet::new));
System.out.println(integerTreeSet);
}
2、流中元素的顺序,如果集合本身无序,则转换的流仍然无序,如HashSet(进入顺序)
/**
* 方法引用
*/
@Test
public void refTest() {
List collect = list.stream().map(u -> u.getName()).collect(Collectors.toList());
System.out.println(collect);
// 等价于 User::getName
System.out.println(list.stream().map(User::getName).collect(Collectors.toList()));
// TreeSet::new
Stream integerStream = Stream.of(1, 2, 3);
TreeSet integerTreeSet = integerStream.collect(Collectors.toCollection(TreeSet::new));
System.out.println(integerTreeSet);
}
3、maxBy(比较器),minBy(..), 找出最大最小值
/**
* 转换成值
*/
@Test
public void biggestGroupTest() {
Optional optional = biggestGroup(artists.stream());
System.out.println(optional.get().getName()); // -> ace
}
public Optional biggestGroup(Stream artists) {
Function getCount = artist -> artist.getMembers().count();
return artists.collect(maxBy(comparing(getCount))); // 找出最大成员数目
}
4、averagingDouble(),求平均值
/**
* 找出一组专辑上曲目的平均数
*/
@Test
public void test() {
System.out.println(averageNumberOfTracks(albums));
}
public double averageNumberOfTracks(List albums) {
return albums.stream().collect(averagingDouble(album -> album.getTrackList().size()));
}
5、数据分块 partitioningBy,根据true, false分成两块数据
/**
* 数据分块 partitioningBy, true or false值划分, 分成true or false 两部分
*/
@Test
public void partitioningByTest() {
Map> booleanListMap = partitioningByNationality(artists.stream());
List trueList = booleanListMap.get(true);
System.out.println(trueList); // out -> each
List falseList = booleanListMap.get(false);
System.out.println(falseList); // out -> ace
}
public Map> partitioningByNationality(Stream artists) {
// 将国籍分成中国和其他两组
return artists.collect(Collectors.partitioningBy(artist -> "china".equals(artist.getNationality())));
}
6、数据分组 groupingBy(),可以根据任意值进行分组
/**
* 数据分组 groupingBy, 任意值划分, 分成true or false 两部分
*/
@Test
public void groupingByTest() {
Map> stringListMap = groupingByNationality(artists.stream());
List list = stringListMap.get("china"); // out -> each
System.out.println(list);
List list1 = stringListMap.get("US"); // out -> ace
System.out.println(list1);
Map> streamListMap = groupingByArtist(albums.stream());
System.out.println(streamListMap.get(artist1)); // 专辑1, 专辑2
}
public Map> groupingByNationality(Stream artists) {
// 按照国籍分组
return artists.collect(Collectors.groupingBy(artist -> artist.getNationality()));
}
/** 按照主唱分组 */
public Map> groupingByArtist(Stream albums) {
return albums.collect(groupingBy(album -> album.getMainMusician()));
}
7、字符串拼装 Collectors.joining, 按照指定的格式拼装字符串
/**
* 输出字符串
*/
@Test
public void joiningTest() {
String joiningName = joiningName(artists.stream());
System.out.println(joiningName); // out -> [ace,each]
}
public String joiningName(Stream artists) {
return artists.map(Artist::getName).collect(Collectors.joining(",", "[", "]"));
}
8、单一的收集器已经能实现很多基础的功能,组合方式更显强大
// 组合收集器
/** ===== 下游收集器: 例子中用到的第二个收集器, 用来收集最终结果的一个子集 ===== */
/**
* 统计某个艺术家专辑数量
*/
@Test
public void combineTest() {
long braden = numberOfAlbums(albums.stream(), "bradenlei");
System.out.println(braden);
}
public long numberOfAlbums(Stream albums, String artistName) {
return albums.map(album -> album.getMusicians())
.filter(musicians -> musicians.filter(musician -> artistName.equals(musician.getName())).count() > 0)
.count();
}
/**
* 计算每个艺术家的专辑数(counting)
*/
@Test
public void combineTest2() {
Map count = count(albums.stream());
System.out.println(count.get(artist1)); // out -> 2
}
public Map count(Stream albums) {
return albums.collect(groupingBy(album -> album.getMainMusician(), counting()));
}
/**
* 收集每个艺术家的专辑名称 (mapping)
*/
@Test
public void combineTest3() {
Map> artistListMap = collectName(albums.stream());
System.out.println(artistListMap.get(artist1)); // out -> [专辑1, 专辑2]
}
public Map> collectName(Stream alubms) {
return alubms.collect(groupingBy(Album::getMainMusician, mapping(Album::getName, toList())));
}
9、有些时候现有的收集器不能满足我们的开发需求,当然也就可以重构和定制收集器了
/** =====重构和定制收集器===== */
/**
* 使用除joining之外的方法拼接字符串
*
* 获取艺术家姓名 (初步)
*/
@Test
public void jointTest() {
StringBuilder reduced = artists.stream()
.map(Artist::getName)
.reduce(new StringBuilder(), (builder, name) -> {
if (builder.length() > 0) builder.append(", ");
return builder.append(name);
}, (left, right) -> left.append(right));
reduced.insert(0, "[");
reduced.append("]");
String result = reduced.toString();
System.out.println(result); // out -> [ace, each]
}
/**
* 优化(自定义StringCombiner)
*/
@Test
public void jointTest2() {
StringCombiner reduced = artists.stream()
.map(Artist::getName)
.reduce(new StringCombiner(", ", "[", "]"),
StringCombiner::add,
StringCombiner::merge); // 这里实际没有调用merge
String string = reduced.toString();
System.out.println(string); // out -> [ace, each]
}
/***
* 进一步优化(使用定制收集器StringCollector收集)
*/
@Test
public void jointTest3() {
String collect = artists.stream()
.map(Artist::getName)
.collect(new StringCollector(", ", "[", "]"));
System.out.println(collect); // out -> [ace, each]
}
/**
* 用reducing代替自定义收集器
*/
@Test
public void jointtTest4() {
// 这种方法的缺点是第二个参数每次都需要new一个对象
StringCombiner collect = artists.stream()
.map(Artist::getName)
.collect(Collectors.reducing(
new StringCombiner(", ", "[", "]"),
name -> new StringCombiner(", ", "[", "]").add(name),
StringCombiner::merge)); // 这里会调用merge, 每次都是两个StringCombiner对象合并, 所以修改StringCombiner.add即可得到正确格式[ace, each]
String s = collect.toString();
System.out.println(s); // out -> [ace[each]
}
自定义类:
public class StringCombiner {
// 前缀
private String prefix;
// 分隔符
private String delim;
// 后缀
private String suffix;
private StringBuilder builder;
public StringCombiner(String delim, String prefix, String suffix) {
this.delim = delim;
this.prefix = prefix;
this.suffix = suffix;
builder = new StringBuilder();
}
public StringCombiner add(String element) {
if(builder.length() > 0) {
builder.append(delim);
} else {
builder.insert(0, prefix);
}
builder.append(element);
return this;
}
public StringCombiner merge(StringCombiner combiner) {
builder.append(combiner.builder);
return this;
}
@Override
public String toString() {
if ("".equals(suffix)) {
return builder.toString();
} else {
int tmp = builder.length();
String result = builder.append(suffix).toString();
builder.setLength(tmp);
return result;
}
}
}
其中:StringCombiner功能仿制java提供的StringJoiner
/**
* String 待收集的元素字符串
* StringCombiner 累加器类型
* String 最终结果类型
*/
public class StringCollector implements Collector {
// 前缀
private String prefix;
// 分隔符
private String delim;
// 后缀
private String suffix;
public StringCollector(String delim, String prefix, String suffix) {
this.delim = delim;
this.prefix = prefix;
this.suffix = suffix;
}
@Override
public Supplier supplier() {
return () -> new StringCombiner(delim, prefix, suffix);
}
@Override
public BiConsumer accumulator() {
return StringCombiner::add;
}
@Override
public BinaryOperator combiner() {
return StringCombiner::merge;
}
@Override
public Function finisher() {
return StringCombiner::toString;
}
@Override
public Set characteristics() {
Set emptySet = Collections.emptySet();
return emptySet;
}
}
10、完整demo:
public class CollectorDemo {
private static User user;
private static List list;
@Before
public void init() {
if (user == null) {
user = new User("张三", 11001);
}
if (list == null) {
list = new ArrayList<>();
list.add(new User("李四", 11002));
list.add(new User("王五", 11003));
list.add(new User("周庄", 11004));
}
}
/**
* 方法引用
*/
@Test
public void refTest() {
List collect = list.stream().map(u -> u.getName()).collect(Collectors.toList());
System.out.println(collect);
// 等价于 User::getName
System.out.println(list.stream().map(User::getName).collect(Collectors.toList()));
// TreeSet::new
Stream integerStream = Stream.of(1, 2, 3);
TreeSet integerTreeSet = integerStream.collect(Collectors.toCollection(TreeSet::new));
System.out.println(integerTreeSet);
}
/**
* 元素顺序, 如果集合本身无序, 则转换的流也是无序的如, HashSet
*/
@Test
public void orderTest() {
// 进来的流无序, 则出去的流也是无序
List origin = Arrays.asList(1, 2, 3, 4);
List integerList = origin.stream().collect(Collectors.toList());
assertEquals(origin, integerList); // ok
Set set = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
Set integerSet = set.stream().collect(Collectors.toSet());
assertEquals(set, integerSet); // 不能保证每次通过
List list = Arrays.asList(2, 1, 4, 3);
List collect = list.stream().sorted().collect(Collectors.toList());
assertEquals(origin, collect); // ok
}
private static List artists = null;
private static List artists2 = null;
private static List