投入:
List<String> elements= new ArrayList<>();
elements.add("Oranges");
elements.add("Figs");
elements.add("Mangoes");
elements.add("Apple");
List<String> listofComments = new ArrayList<>();
listofComments.add("Apples are better than Oranges");
listofComments.add("I love Mangoes and Oranges");
listofComments.add("I don't know like Figs. Mangoes are my favorites");
listofComments.add("I love Mangoes and Apples");输出:芒果,苹果,橘子,无花果->输出必须按元素出现次数的降序排列。如果元素看起来相等,则为no。它们必须按字母顺序排列。
我刚接触过Java 8,遇到了这个问题。我试着部分地解决它,但我无法解决它。有人能帮我找到更好的代码吗?
我的代码:
Function<String, Map<String, Long>> function = f -> {
Long count = listofComments.stream()
.filter(e -> e.toLowerCase().contains(f.toLowerCase())).count();
Map<String, Long> map = new HashMap<>(); //creates map for every element. Is it right?
map.put(f, count);
return map;
};
elements.stream().sorted().map(function).forEach(e-> System.out.print(e));输出:{Apple=2}{Figs=1}{Mangoes=3}{Oranges=2}
发布于 2020-02-28 08:51:34
在现实生活中,您必须考虑到,对任意数量的注释应用任意数量的匹配操作,在数量增加时可能会变得安静、昂贵,因此值得做一些准备:
Map<String,Predicate<String>> filters = elements.stream()
.sorted(String.CASE_INSENSITIVE_ORDER)
.map(s -> Pattern.compile(s, Pattern.LITERAL|Pattern.CASE_INSENSITIVE))
.collect(Collectors.toMap(Pattern::pattern, Pattern::asPredicate,
(a,b) -> { throw new AssertionError("duplicates"); }, LinkedHashMap::new));即使不执行正则匹配,Predicate类也是很有价值的。LITERAL和CASE_INSENSITIVE标志的组合支持具有预期语义的搜索,而无需将整个字符串转换为小写(顺便说一句,这并不足以满足所有可能的情况)。对于这种匹配,准备工作将包括为Boyer-Moore算法构建必要的数据结构,以便在内部进行更有效的搜索。
这张地图可以重复使用。
对于你的具体任务,一种使用它的方法是
filters.entrySet().stream()
.map(e -> Map.entry(e.getKey(), listofComments.stream().filter(e.getValue()).count()))
.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
.forEachOrdered(e -> System.out.printf("%-7s%3d%n", e.getKey(), e.getValue()));它将为示例数据打印如下:
Mangoes 3
Apple 2
Oranges 2
Figs 1请注意,filters映射已经按字母顺序排序,第二个流操作的sorted对于具有定义的遭遇顺序的流是https://en.wikipedia.org/wiki/Sorting_algorithm#Stability,因此它只需要按事件排序,具有相同元素的条目将保持相对顺序,这是源映射中的字母顺序。
Map.entry(…)需要JavaAversion9或更高版本。对于Java 8,您必须使用以下内容
取而代之的是new AbstractMap.SimpleEntry(…)。
发布于 2020-02-28 02:58:36
您仍然可以修改您的函数来存储Map.Entry,而不是完整的Map。
Function<String, Map.Entry<String, Long>> function = f -> Map.entry(f, listOfComments.stream()
.filter(e -> e.toLowerCase().contains(f.toLowerCase())).count());然后在执行终端操作(在您的示例中为forEach打印)之前,对这些条目进行排序。
elements.stream()
.map(function)
.sorted(Comparator.comparing(Map.Entry<String, Long>::getValue)
.reversed().thenComparing(Map.Entry::getKey))
.forEach(System.out::println);然后,这将为您提供以下输出:
Mangoes=3
Apples=2
Oranges=2
Figs=1发布于 2020-02-28 00:18:09
第一件事是声明一个额外的类。它将保存元素并计数:
class ElementWithCount {
private final String element;
private final long count;
ElementWithCount(String element, long count) {
this.element = element;
this.count = count;
}
String element() {
return element;
}
long count() {
return count;
}
}要计算count,让我们声明一个额外的函数:
static long getElementCount(List<String> listOfComments, String element) {
return listOfComments.stream()
.filter(comment -> comment.contains(element))
.count();
}因此,要找到结果,我们需要将元素流转换为ElementWithCount对象流,然后按计数对该流进行排序,然后将其转换回元素流,并将其收集到结果列表中。
为了简化这项任务,让我们将比较器定义为一个单独的变量:
Comparator<ElementWithCount> comparator = Comparator
.comparing(ElementWithCount::count).reversed()
.thenComparing(ElementWithCount::element);现在,当所有部分都准备就绪时,最后的计算就很容易了:
List<String> result = elements.stream()
.map(element -> new ElementWithCount(element, getElementCount(listOfComments, element)))
.sorted(comparator)
.map(ElementWithCount::element)
.collect(Collectors.toList());您可以使用Map.Entry代替单独的类和内联getElementCount,因此它将是“单行”解决方案:
List<String> result = elements.stream()
.map(element ->
new AbstractMap.SimpleImmutableEntry<>(element,
listOfComments.stream()
.filter(comment -> comment.contains(element))
.count()))
.sorted(Map.Entry.<String, Long>comparingByValue().reversed().thenComparing(Map.Entry.comparingByKey()))
.map(Map.Entry::getKey)
.collect(Collectors.toList());但是在这种形式下很难理解,所以我建议把它分成逻辑部分。
https://stackoverflow.com/questions/60443274
复制相似问题