我目前有一个多层结构数据,如下所示:
行业类有一个私有字段
Set<Company>
,可以为空。公司类有一个私有字段
Set<Division>
,可以为空。Division类有一个私有字段
Set<Group>
,可以为null。组类有一个私有字段
groupName
,可以为null 可以使用getter(getGroupName()
)检索。
我正在尝试将Industry的实例一直流式传输到Group层,并使用&#34; /&#34;将所有groupName连接到一个String中。介于两者之间。
如果此行业实例未包含任何groupName,请返回字符串&#34; null&#34;。
基于我对Java 8的有限知识,我正在考虑这样的编码:
industry.stream()
.flatmap(industry -> industry.getCompanies().stream())
.filter(Objects::nonNull)
.flatmap(company -> company.getDivisions().stream())
.filter(Objects::nonNull)
.flatmap(division -> division.getGroups().stream())
.map(group -> group.getGroupName)
.collect(Collectors.joining("/")));
这段代码似乎有些瑕疵。另外,我不知道在哪里添加声明,如果Industry无法检索任何groupName,而不是将所有groupName连接成一个字符串,只需返回一个字符串&#34; null&#34;。
在我的情况下使用Java 8流的正确方法是什么?
感谢。
答案 0 :(得分:5)
Collectors.joining(…)
基于班级StringJoiner
。它提供了分隔符,前缀和后缀功能,但遗憾的是无法提供空值。
要添加该功能,我们必须重新实施Collectors.joining
,感谢使用StringJoiner
时不那么难。
更改流操作的最后一行
.collect(Collectors.joining("/"));
到
.filter(Objects::nonNull) // elide all null elements
.collect(()->new StringJoiner("/", "", "").setEmptyValue("null"), // use "null" when empty
StringJoiner::add, StringJoiner::merge).toString();
答案 1 :(得分:0)
我理解你的问题几乎任何东西都可以为空。在这种情况下,您可以创建自己的函数来处理这个问题。我这样做了一个:
/**
* Creates a stream function for the provided collection function which ignores all null values.
* Will filter out null values passed into the collection function and null values from the resulting stream
* @param collectionFn
* @param <T>
* @param <R>
* @return
*/
public static <T, R> Function<T, Stream<R>> nullSafeMapper(Function<T, Collection<R>> collectionFn) {
return (v) -> Optional.ofNullable(v)
.map(collectionFn)
.map(Collection::stream)
.orElse(Stream.empty())
.filter(Objects::nonNull);
}
基本上它是完全空的安全,过滤掉输入和输出中为空的任何东西。并可以这样使用:
industries.stream()
.flatMap(SO46101593.nullSafeMapper(Industry::getCompanies))
.flatMap(SO46101593.nullSafeMapper(Company::getDivisions))
.flatMap(SO46101593.nullSafeMapper(Division::getGroups))
.map(group -> group.getGroupName())
.filter(Objects::nonNull) // filter out null group names
.collect(Collectors.joining("/"));
你也可以把这个逻辑直接推到你的表达式中,但由于它必须重复3次才会有点......冗长和重复
答案 2 :(得分:0)
以下是空检查的示例:
String s = industries.stream()
.filter( i -> i.getCompanies() != null ).flatMap( i -> i.getCompanies().stream() )
.filter( c -> c != null && c.getDivisions() != null ).flatMap( c -> c.getDivisions().stream() )
.filter( d -> d != null && d.getGroups() != null ).flatMap( d -> d.getGroups().stream() )
.filter( g -> g != null && g.getGroupName() != null ).map( g -> g.getGroupName() )
.collect( Collectors.joining("/") );
您可以使用Holger的示例替换Collectors.joining("/")
。
答案 3 :(得分:0)
这应该这样做:
Stream.of(industry)
.map(Industry::getCompanies).filter(Objects::nonNull)
.flatMap(Set::stream)
.map(Company::getDivisions).filter(Objects::nonNull)
.flatMap(Set::stream)
.map(Division::getGroups).filter(Objects::nonNull)
.flatMap(Set::stream)
.map(Group::getGroupName).filter(Objects::nonNull)
.collect(Collectors.collectingAndThen(Collectors.joining("/"),
names -> names.isEmpty() ? "null" : names));
我假设industry.stream()
不正确,因为你说你是在“工业实例”工作。相反,我使用一个元素创建Stream<Industry>
。
在尝试对它们调用stream
之前,您需要对集合进行空检查。你正在检查stream
是否返回null,这已经太晚了。
从空结果到“null”的最终转换属于Collector
中“终结者”功能的概念。 Collectors.joining
不允许您直接指定整理程序,但可以使用Collectors.collectingAndThen
将整理程序添加到任何现有Collector
。