del*_*ica 6 java partitioning java-stream
假设我有一个包含以下元素的列表:
List<String> endings= Arrays.asList("AAA", "BBB", "CCC", "DDD");
Run Code Online (Sandbox Code Playgroud)
我还有一个很大的字符串列表,从中我想从上面的列表中选择所有以任何字符串结尾的元素。
List<String> fullList= Arrays.asList("111.AAA", "222.AAA", "111.BBB", "222.BBB", "111.CCC", "222.CCC", "111.DDD", "222.DDD");
Run Code Online (Sandbox Code Playgroud)
理想情况下,我想对第二个列表进行分区,以使其包含四个组,每个组仅包含那些以第一个列表中的一个字符串结尾的元素。因此,在上述情况下,结果将是4组,每组2个元素。
我找到了这个示例,但是我仍然缺少可以过滤包含在不同列表中的所有结尾的部分。
Map<Boolean, List<String>> grouped = fullList.stream().collect(Collectors.partitioningBy((String e) -> !e.endsWith("AAA")));
Run Code Online (Sandbox Code Playgroud)
更新:MC Emperor的答案确实有效,但是它在包含数百万个字符串的列表上崩溃,因此在实践中效果不佳。
这与原始答案中的方法类似,但是现在fullList不再遍历多次。而是遍历一次,并且对于每个元素,在结尾列表中搜索匹配项。它映射到一个Entry(ending, fullListItem),然后按列表项分组。分组时,值元素被展开为List。
Map<String, List<String>> obj = fullList.stream()
.map(item -> endings.stream()
.filter(item::endsWith)
.findAny()
.map(ending -> new AbstractMap.SimpleEntry<>(ending, item))
.orElse(null))
.filter(Objects::nonNull)
.collect(groupingBy(Map.Entry::getKey, mapping(Map.Entry::getValue, toList())));
Run Code Online (Sandbox Code Playgroud)
您可以使用此:
Map<String, List<String>> obj = endings.stream()
.map(ending -> new AbstractMap.SimpleEntry<>(ending, fullList.stream()
.filter(str -> str.endsWith(ending))
.collect(Collectors.toList())))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
Run Code Online (Sandbox Code Playgroud)
它采用所有结尾,并遍历fullList以该值结尾的for元素。
请注意,使用这种方法,对于每个元素,它遍历整个列表。这是相当低效的,我认为您最好使用另一种方式映射元素。例如,如果您对中的元素结构有所了解fullList,则可以立即对其进行分组。
要对流进行分区,意味着将每个元素放入两个组之一。由于后缀更多,因此您希望分组,即使用groupingBy代替partitioningBy。
如果要支持任意endings列表,则可能更喜欢线性搜索。
一种方法是使用排序后的集合,并使用基于后缀的比较器。
比较器可以像
Comparator<String> backwards = (s1, s2) -> {
for(int p1 = s1.length(), p2 = s2.length(); p1 > 0 && p2 > 0;) {
int c = Integer.compare(s1.charAt(--p1), s2.charAt(--p2));
if(c != 0) return c;
}
return Integer.compare(s1.length(), s2.length());
};
Run Code Online (Sandbox Code Playgroud)
逻辑类似于字符串的自然顺序,唯一的区别是它从末尾开始。换句话说,它等效于Comparator.comparing(s -> new StringBuilder(s).reverse().toString()),但效率更高。
然后,输入
List<String> endings= Arrays.asList("AAA", "BBB", "CCC", "DDD");
List<String> fullList= Arrays.asList("111.AAA", "222.AAA",
"111.BBB", "222.BBB", "111.CCC", "222.CCC", "111.DDD", "222.DDD");
Run Code Online (Sandbox Code Playgroud)
您可以按照以下方式执行任务
// prepare collection with faster lookup
TreeSet<String> suffixes = new TreeSet<>(backwards);
suffixes.addAll(endings);
// use it for grouping
Map<String, List<String>> map = fullList.stream()
.collect(Collectors.groupingBy(suffixes::floor));
Run Code Online (Sandbox Code Playgroud)
但是,如果您只对每个组的计数感兴趣,则在分组时应该正确计数,避免存储元素列表:
Map<String, Long> map = fullList.stream()
.collect(Collectors.groupingBy(suffixes::floor, Collectors.counting()));
Run Code Online (Sandbox Code Playgroud)
如果列表可以包含与列表后缀不匹配的字符串,则必须替换suffixes::floor为s -> { String g = suffixes.floor(s); return g!=null && s.endsWith(g)? g: "_None"; }或类似的函数。
如果您的fullList某些元素的后缀不存在于您的元素中,endings您可以尝试以下操作:
List<String> endings= Arrays.asList("AAA", "BBB", "CCC", "DDD");
List<String> fullList= Arrays.asList("111.AAA", "222.AAA", "111.BBB", "222.BBB", "111.CCC", "222.CCC", "111.DDD", "222.DDD", "111.EEE");
Function<String,String> suffix = s -> endings.stream()
.filter(e -> s.endsWith(e))
.findFirst().orElse("UnknownSuffix");
Map<String,List<String>> grouped = fullList.stream()
.collect(Collectors.groupingBy(suffix));
System.out.println(grouped);
Run Code Online (Sandbox Code Playgroud)