Учитывая список строк, возможно ли получить в одной строке сопоставление каждой длины с набором строк этой длины, отсортированных по длине?

Я придумал следующее, которое действительно работает, но я чувствую, что должен быть более чистый однострочный способ, который не полагается на "внешнюю" карту (result ниже):

public TreeMap<Integer, HashSet<String>> mapLenToString(List<String> strings){
    TreeMap<Integer, HashSet<String>> result = new TreeMap<>();
    strings.stream()
           .forEach(s -> { 
                int len = s.length();
                if (result.containsKey(len)) {
                    HashSet<String> larger = result.get(len);
                    larger.add(s);
                    result.replace(len, larger);
                }
                else {
                    HashSet<String> newSet = new HashSet<>();
                    newSet.add(s);
                    result.put(len, newSet);
                }
                
            });
    return result;
}

2 ответа

Решение

Просто используйте groupingByколлектор. Вы можете управлять типом карты и типом набора, в который собираются элементы.

TreeMap<Integer, HashSet<String>> result = strs
  .stream()
  .collect(
    Collectors.groupingBy(
      s -> s.length(),
      TreeMap::new,
      Collectors.toCollection(HashSet::new)
    )
  );

Используя это

List<String> strs = Arrays.asList(
  "DEF", "ABC", "Hello world", "z", 
  "a", "q", "90", "12345678910", "ab");

выход

{1=[a, q, z], 2=[90, ab], 3=[ABC, DEF], 11=[12345678910, Hello world]}

Ссылка на repl.it


Вы также можете использовать Collectors.toSet() вместо того toCollection(HashSet::new), где реализация по умолчанию HashSet (но, вероятно, не гарантируется)

Альтернатива с использованием Guava Multimaps:com.google.common.collect.Multimaps.index (итерация, функция)-

Multimap<Integer, String> index =
       Multimaps.index(strs,  s -> s.length());

К сожалению, он не отсортирован по результату.

Другие вопросы по тегам