简体   繁体   English

列表<string>获取以另一个列表中的一个字符串结尾的所有元素的计数</string>

[英]List<String> get count of all elements ending with one of strings from another list

Let's say I have one list with elements like:假设我有一个包含以下元素的列表:

List<String> endings= Arrays.asList("AAA", "BBB", "CCC", "DDD");

And I have another large list of strings from which I would want to select all elements ending with any of the strings from the above list.我还有另一个大的字符串列表,我想从中 select 所有以上述列表中的任何字符串结尾的元素。

List<String> fullList= Arrays.asList("111.AAA", "222.AAA", "111.BBB", "222.BBB", "111.CCC", "222.CCC", "111.DDD", "222.DDD");

Ideally I would want a way to partition the second list so that it contains four groups, each group containing only those elements ending with one of the strings from first list.理想情况下,我想要一种对第二个列表进行分区的方法,使其包含四个组,每个组仅包含那些以第一个列表中的一个字符串结尾的元素。 So in the above case the results would be 4 groups of 2 elements each.因此,在上述情况下,结果将是 4 组,每组 2 个元素。

I found this example but I am still missing the part where I can filter by all endings which are contained in a different list.我找到了这个示例,但我仍然缺少可以按包含在不同列表中的所有结尾进行过滤的部分。

Map<Boolean, List<String>> grouped = fullList.stream().collect(Collectors.partitioningBy((String e) -> !e.endsWith("AAA")));

UPDATE: MC Emperor's Answer does work, but it crashes on lists containing millions of strings, so doesn't work that well in practice.更新: MC Emperor's Answer 确实有效,但它在包含数百万个字符串的列表上崩溃,因此在实践中效果不佳。

Update更新

This one is similar to the approach from the original answer, but now fullList is no longer traversed many times.这与原始答案中的方法类似,但现在不再遍历fullList多次。 Instead, it is traversed once, and for each element, the list of endings is searched for a match.相反,它会被遍历一次,并且对于每个元素,都会在结尾列表中搜索匹配项。 This is mapped to an Entry(ending, fullListItem) , and then grouped by the list item.这被映射到一个Entry(ending, fullListItem) ,然后按列表项分组。 While grouping, the value elements are unwrapped to a List .分组时,值元素被展开为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())));

Original answer原始答案

You could use this:你可以使用这个:

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));

It takes all endings and traverses the fullList for elements ending with the value.它采用所有结尾并遍历以该值fullList的元素的完整列表。

Note that with this approach, for each element it traverses the full list.请注意,使用这种方法,对于每个元素,它都会遍历完整列表。 This is rather inefficient, and I think you are better off using another way to map the elements.这是相当低效的,我认为你最好使用另一种方式来 map 元素。 For instance, if you know something about the structure of the elements in fullList , then you can group it immediately.例如,如果您对fullList中元素的结构有所了解,那么您可以立即对其进行分组。

To partition a stream, means putting each element into one of two groups.对 stream 进行分区意味着将每个元素放入两组中的一组。 Since you have more suffixes, you want grouping instead, ie use groupingBy instead of partitioningBy .由于您有更多后缀,因此您希望进行分组,即使用groupingBy而不是partitioningBy

If you want to support an arbitrary endings list, you might prefer something better than a linear search.如果您想支持任意endings列表,您可能更喜欢比线性搜索更好的东西。

One approach is using a sorted collection, using a suffix-based comparator.一种方法是使用排序集合,使用基于后缀的比较器。

The comparator can be implemented like比较器可以像这样实现

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());
};

The logic is similar to the natural order of string, with the only difference that it runs from the end to the beginning.逻辑类似于字符串的自然顺序,唯一的区别是它从结尾运行到开头。 In other words, it's equivalent to Comparator.comparing(s -> new StringBuilder(s).reverse().toString()) , but more efficient.换句话说,它相当于Comparator.comparing(s -> new StringBuilder(s).reverse().toString()) ,但效率更高。

Then, given an input like然后,给定一个输入

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");

you can perform the task as您可以将任务执行为

// 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));

But if you are only interested in the count of each group, you should count right while grouping, avoiding to store lists of elements:但是,如果您只对每个组的计数感兴趣,则应该在分组时正确计数,避免存储元素列表:

Map<String, Long> map = fullList.stream()
    .collect(Collectors.groupingBy(suffixes::floor, Collectors.counting()));

If the list can contain strings which match no suffix of the list, you have to replace suffixes::floor with s -> { String g = suffixes.floor(s); return g.=null && s?endsWith(g): g; "_None"; }如果列表可以包含不匹配列表后缀的字符串,则必须将suffixes::floor替换为s -> { String g = suffixes.floor(s); return g.=null && s?endsWith(g): g; "_None"; } s -> { String g = suffixes.floor(s); return g.=null && s?endsWith(g): g; "_None"; } s -> { String g = suffixes.floor(s); return g.=null && s?endsWith(g): g; "_None"; } or a similar function. s -> { String g = suffixes.floor(s); return g.=null && s?endsWith(g): g; "_None"; }或类似的 function。

Use groupingBy .使用groupingBy

Map<String, List<String>> grouped = fullList
  .stream()
  .collect(Collectors.groupingBy(s -> s.split("\\.")[1]));

s.split("\\.")[1] will take the yyy part of xxx.yyy . s.split("\\.")[1]将采用xxx.yyyyyy部分。

EDIT: if you want to empty the values for which the ending is not in the list, you can filter them out:编辑:如果您想清空其结尾不在列表中的值,您可以将它们过滤掉:

grouped.keySet().forEach(key->{
  if(!endings.contains(key)){
    grouped.put(key, Collections.emptyList());
  }
});

If you create a helper method getSuffix() that accepts a String and returns its suffix (for example getSuffix("111.AAA") will return "AAA" ), you can filter the String s having suffix contained in the other list and then group them:如果您创建一个辅助方法getSuffix()接受String并返回其后缀(例如getSuffix("111.AAA")将返回"AAA" ),您可以过滤具有包含在另一个列表中的后缀的String ,然后将它们分组:

Map<String,List<String>> grouped =
    fullList.stream()
            .filter(s -> endings.contains(getSuffix(s)))
            .collect(Collectors.groupingBy(s -> getSuffix(s)));

For example, if the suffix always begins at index 4, you can have:例如,如果suffix总是从索引 4 开始,您可以:

public static String getSuffix(String s) {
    return s.substring(4);
}

and the above Stream pipeline will return the Map :上面的Stream管道将返回Map

{AAA=[111.AAA, 222.AAA], CCC=[111.CCC, 222.CCC], BBB=[111.BBB, 222.BBB], DDD=[111.DDD, 222.DDD]}

PS note that the filter step would be more efficient if you change the endings List to a HashSet . PS 请注意,如果将endings List更改为HashSet ,则filter步骤会更有效。

If your fullList have some elements which have suffixes that are not present in your endings you could try something like:如果您的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);

You can use groupingBy with filter on endings list as,您可以将groupingByendings列表上的过滤器一起使用,

fullList.stream()
  .collect(groupingBy(str -> endings.stream().filter(ele -> str.endsWith(ele)).findFirst().get()))

One can use groupingBy of substrings with filter to ensure that the final Map has just the Collection of relevant values.可以使用带有filter的子字符串的groupingBy来确保最终的Map具有相关值的Collection This could be sone as:这可能是:

Map<String, List<String>> grouped = fullList.stream()
        .collect(Collectors.groupingBy(a -> getSuffix(a)))
        .entrySet().stream()
        .filter(e -> endings.contains(e.getKey()))
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

private static String getSuffix(String a) {
    return a.split(".")[1];
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 一个列表中N个元素与另一列表中M个元素的所有可能组合 - All possible combinations of N elements from one list, with M elements from another list 从数组列表中获取所有以指定字符串开头的元素 - Get all the elements which are starting with a specified string from an Array List 删除一个列表中的所有元素,然后添加到另一列表-Java - Remove all elements in one list and add to another list - Java 将一个链接列表中的所有元素追加到另一列表的末尾 - Appending all elements in one Linked-list to the end of another list 在Java中从“一个”字符串开始直到“另一个”字符串的字符串列表进行迭代 - Iterate through a List of Strings starting from 'One' String until 'Another' String in Java 如何找到该列表包含另一个列表中的所有元素? - how to find that the list contains all elements from the another list? 根据另一个列表中的条件删除一个列表中的某些元素 - Remove certain elements in one list based on condition from another list 从集合的所有文档中获取数组列表元素的总数 - Getting the total count of array list elements from all documents of a collection 从另一个具有指定ID的列表中获取元素的列表 - Get List of elements from another List with specified ids Java 8 Streams:计算元素的出现次数(列表<String> list1) 来自文本数据列表(List<String> 清单 2) - Java 8 Streams : Count the occurrence of elements(List<String> list1) from list of text data(List<String> list2)
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM