Скажем, результат моего запроса такой

MyClass(name=AAA, action=action1, price=5),
MyClass(name=BBB, action=action13, price=7),
MyClass(name=AAA, action=action31, price=2)

И хотите создать карту, сгруппированную по name, где значением будет набор (k, v) на action и price, как показано ниже

(AAA=((action1, 5),(action31, 2)), BBB=(action13, 7))

Поэтому пытался, как показано ниже, но я получаю сообщение об ошибке -> "нестатический метод не может быть передан из статического содержимого" при попытке использовать Map.Entry

Stream<Object> trying = results
    .stream()
    .flatMap(it -> {
          Map<String,String> innerMap = new HashMap<>();
          innerMap.put(it.getAction(),it.getPrice());
          Map<String,Map<String,String>> upperMap = new HashMap<>();
          upperMap.put(it.getName(), innerMap);
          return upperMap.entrySet().stream();
        }
    ) 
    .collect(Collectors.groupingBy(Map.Entry::getKey,
        Collectors.mapping(Map.Entry::getValue,
            Collectors.toList())));
1
user3123610 10 Июл 2021 в 17:48

4 ответа

Лучший ответ

Вы можете предоставить Collector вместе с groupingBy для создания внутренней карты: (Предположим статический импорт для Collectors.groupingBy и Collectors.toMap)

results.stream()
       .collect(groupingBy(MyClass::getName,
                           toMap(MyClass::getAction,MyClass::getPrice)));       
1
Gautham M 10 Июл 2021 в 15:36

Почему? Иметь Map<String, Map<String, String>> - это катастрофа с набором текста. Ява номинальная; язык работает лучше, если вы используете то, в чем он хорош, а это не так.

Описание вашей проблемы неоднозначно. Представьте себе следующий ввод:

MyClass(name=AAA, action=action1, price=5),
MyClass(name=BBB, action=action13, price=7),
MyClass(name=AAA, action=action1, price=2)

Какой должен быть результат?

  • Исключение
  • (AAA=((action1, 2)), BBB=(action13, 7))
  • (AAA=((action1, 5)), BBB=(action13, 7))
  • (AAA=((action1, [2, 5])), BBB=(action13, [7]))

Последнее несколько разумно, потому что другие, особенно №2 и №3, совершенно произвольны. Даже если вам нужен один из них, надеюсь, он поможет вам осознать, что это будет непростая и понятная серия функциональных операций, и, скорее всего, вы вообще не захотите использовать потоки, потому что не используете их. приведет к более читаемому коду.

Подписи ни в коем случае не совпадают с тем, что вы написали; Я предполагаю, что вы напортачили (похоже, вы установили тип варианта price как String, что кажется немного сумасшедшим. В этом коде я представлю его как Integer, представляющие атомные единицы (евроценты, сатоши и т. д.).

Если вы действительно этого хотите (а я очень сомневаюсь, что это так!), Давайте разберемся по шагам:

  • Сначала вы хотите сгруппировать по, указав имя (AAA) в качестве ключа группы, а затем список экземпляров MyClass.
  • Затем вы хотите сгруппировать по снова , сгруппировав все экземпляры MyClass с одним и тем же именем действия вместе, чтобы в итоге получить этот целочисленный список [2, 5].
  • Вы также хотите сопоставить свой экземпляр MyClass только с его ценой где-нибудь в этом процессе.

Просматривая API потокового материала, в частности Collectors, мы получаем нужный вам вызов:

groupingBy(Function<? super T,? extends K> classifier, Collector<? super T,A,D> downstream)

Возвращает Collector, реализующий каскадную операцию «group by» для входных элементов типа T, группируя элементы в соответствии с функцией классификации, а затем выполняя операцию сокращения для значений, связанных с данным ключом, с использованием указанного нижестоящего Collector.

Итак, приступим к делу, если вам нужен 4-й вариант:

@lombok.Value public class MyClass {
  String name, action;
  int price;
}

List<MyClass> results = List.of(
  new MyClass("AAA", "action1", 5),
  new MyClass("BBB", "action2", 4),
  new MyClass("AAA", "action1", 3),
  new MyClass("AAA", "action3", 2));

Map<String, Map<String, List<MyClass>>> answer =
  results.stream()
  .collect(Collectors.groupingBy(MyClass::getName,
    Collectors.groupingBy(MyClass::getAction)));

System.out.println(answer);

Это хороший первый шаг, но вместо списка экземпляров MyClass вам нужен просто список целых чисел. Мы тоже можем это сделать:

Map<String, Map<String, List<Integer>>> answer =
  results.stream()
  .collect(Collectors.groupingBy(MyClass::getName,
    Collectors.groupingBy(MyClass::getAction,
      Collectors.collectingAndThen(Collectors.toList(),
     listOfMc -> listOfMc.stream().map(MyClass::getPrice).collect(Collectors.toList())
  ))));

И это дает вам:

{AAA={action1=[5, 3], action3=[2]}, BBB={action2=[4]}}

Думаю, именно то, что вы хотели.

Но затем взгляните на этот код и поймите, что где-то, каким-то образом, вы сделали неправильный выбор: P - это довольно далеко от легко читаемого кода!

0
rzwitserloot 10 Июл 2021 в 15:44

Вам нужно полностью обработать их. Вот один чистый и читаемый способ: -



public class Builder {

    public static void main(String[] args) throws IOException {
        List<MyClass> list = Arrays.asList(new MyClass("AAA", "action1", 5)
                , new MyClass("BBB", "action13", 7), new MyClass("AAA", "action31", 2));

        Map<String, List<Pairs>> listList = new HashMap<String, List<Pairs>>();

        list.stream().map(l -> {
            List<Pairs> pairs = listList.getOrDefault(l.name, new ArrayList<>());

            pairs.add(new Pairs(l.action, l.price));
            listList.put(l.name, pairs);

            return pairs;
        }).collect(Collectors.toList());

        System.out.println(listList);
//        {AAA=[Pairs{action='action1', price=5}, Pairs{action='action31', price=2}], BBB=[Pairs{action='action13', price=7}]}

    }

}

class Pairs {
  String action;
  int price;

    public Pairs(String action, int price) {
        this.action = action;
        this.price = price;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("Pairs{");
        sb.append("action='").append(action).append('\'');
        sb.append(", price=").append(price);
        sb.append('}');
        return sb.toString();
    }
}

class MyClass {
    String name;
    String action;
    int price = 5;

    public MyClass(String name, String action, int price) {
        this.name = name;
        this.action = action;
        this.price = price;
    }
}


0
Manish Soni 10 Июл 2021 в 15:27

Я предлагаю попробовать использовать карту типа Map<String, List<MyClass>> так будет легче управлять.

Вот идея, как это можно закодировать.

import java.util.*;

public class MyClass {
    String name;
    String action;
    int price;

    public MyClass(String name, String action, int price) {
        this.name = name;
        this.action = action;
        this.price = price;
    }

    @Override
    public String toString() {
        return  "{" + name + ", " + action + ", " + price +"}";
    }

    public static void main(String[] args) {
        Map<String, List<MyClass>> map = new HashMap<>();
        
        MyClass[] objs = {  
                            new MyClass("AAA", "action1", 5),
                            new MyClass("BBB", "action2", 7),
                            new MyClass("AAA", "action3", 2)
                        };

        for(MyClass myClass: objs) {
            if(!map.containsKey(myClass.name)) {
                map.put(myClass.name, new ArrayList<MyClass>());
            }
            map.get(myClass.name).add(myClass);
        }
        
        System.out.println(map);
    }
}
0
Lovesh Dongre 10 Июл 2021 в 15:37