LINUX.ORG.RU

Набивка значений из двух списков в Map

 


0

2

Суть - есть два списка, элементы одного ссылаются на другой (вернее соотносятся, а не ссылаются, ссылки на объекты разные т.к. списки из базы приходят). Грубо говоря отношение Many-To-One со стороный элементов первого к элементам второго. Классы элементов например такие как ниже.

public class ElemA {
  public ElemB b;
  public double value;
}

public class ElemB {
  public int id;
  public String name;
}

В итоге хотелось бы получать Map<String, Double> с именами из второго списка и суммой значений первого, который потом отправляется дальше по этапу.

Пока придумал так:

for (ElemB b : bList) {
 double vals = aList.stream().filter((a) -> a.b.id == b.id).mapToDouble((a) -> a.value).sum();
 map.put(b.name, new Double(vals));
}

Вот возник вопрос - а не туплю ли я? Можно ли сделать это проще/эффективнее/лучше? Иду по списку B т.к. во-первых он логически выше, а во-вторых в целом может вылезти null или еще какой рудимент из базы, хотя по идее чистка должна идти каскадом.

И кстати, раз уж написал - что корректнее использовать, public параметры как в примере или private + Get/Set? Сейчас юзаю второе, просто если напишу так - пост расползется. Просто пока копался в библиотеке к гугловому API заметил что народ юзает там везде public, хотя в большинстве библиотек вижу private + get/set

★★★★★

Последнее исправление: upcFrost (всего исправлений: 2)

Норм вроде, можно и по ElemA пройтись (java7, HashMap):

for(ElemA a : aList)
{
    B b = a.getB;
    map.put(b, map.containsKey(b) ? map.get(b) + a.getValue : a.getValue);
}

Get/set считается стандартом, хоть и громоздко, но правильно.

alchemist
()
Последнее исправление: alchemist (всего исправлений: 1)

Вот такое в голову пришло, за работоспособность не ручаюсь.

Map<Integer, Double> bIdToSumOfValues = listA.stream().collect(Collectors.toMap(a -> a.b.id, a -> a.value, Double::sum));
Map<String, Double> result = listB.stream().collect(Collectors.toMap(b -> b.name, b -> bIdToSumOfValues.get(b.id)));

Что касается полей, то лучше делать их private, с public-геттерами и public-сеттерами. Если лень писать эти методы, то можно посмотреть в сторону Project Lombok (генерация тривиального кода), но, на мой взгляд, это костыль.

CARS ★★★★
()
Последнее исправление: CARS (всего исправлений: 1)
Ответ на: комментарий от CARS

Если лень писать эти методы, то можно посмотреть в сторону Project Lombok (генерация тривиального кода), но, на мой взгляд, это костыль.

Не, нафиг генераторы. Уже намучался...

upcFrost ★★★★★
() автор топика
Последнее исправление: upcFrost (всего исправлений: 1)

сложность алгоритма n*m. не юзай стримы. создай мап b.id -> {sum}, проходись по a, бери запись из этой мапы по a.b.id, обновляй новым значением суммы. вроде так. потом, если надо, переложишь по b.name -> sum

dzidzitop ★★
()

for (ElemB b : bList) {
double vals = aList.stream().

а потом жалуются шо жава тормозит

1) определи сколько объектов может прилететь

2) чего больше ElemB или ElemA

3) старайся не создавать объекты в цикле (особенно если у тебя мульен на мульен записей), а создание стримов в цикле это вообще графитовый песец

Deleted
()
Последнее исправление: Deleted (всего исправлений: 1)
Ответ на: комментарий от alchemist

Говорят, java в определенном смысле сопоставима с c#, в последнем, например:

aList.GroupBy(a => a.b.name).ToDictionary(g => g.Key, g => g.Sum(a => a.value));

omegatype ★★★
()

В такой постановке (b внутри a), лучше чем в первом ответе наверное не сделать.

Midael ★★★★★
()
Ответ на: комментарий от Deleted

цикл по B еще и потому что элементов B штуки 4-5, а вот A может быть дохрена. я знаю что создание стрима достаточно тяжелая вещь. в итоге чуть переделал запрос к базе, теперь стрим создается только 1 раз. просто без него фильтрация и прочие сравнения становятся головной болью, там добавилось еще одно условие для проверки.

но наверно попробую переписать еще раз без стрима

кстати, а как вообще быстродействие между скажем циклом по итератору, for (ElemA el : listA) и лямбдой в forEach?

upcFrost ★★★★★
() автор топика
Последнее исправление: upcFrost (всего исправлений: 2)
Ответ на: комментарий от Deleted

computeIfPresent может? Правда смотрю на документацию и на default implementation, и не понимаю, как это может быть проще и быстрее, чем ответ алхимика

Midael ★★★★★
()
Последнее исправление: Midael (всего исправлений: 1)
Ответ на: комментарий от Midael

computeIfPresent

да

Правда смотрю на документацию и на default implementation,

http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/...

и не понимаю, как это может быть проще и быстрее, чем ответ алхимика

ну если ты не видишь разницу между поиском в хештаблице один раза и два раза то что мне еще поделать?

Deleted
()
Ответ на: комментарий от Deleted

Теперь вижу. Правда тогда получается, что нужен просто compute - для остальных вариантов надо мапу предварительно забивать значениями.

Midael ★★★★★
()
Последнее исправление: Midael (всего исправлений: 1)
Ответ на: комментарий от Midael

Правда тогда получается, что нужен просто compute -

вообще говоря чтобы не плодить Double надо так

map.computeIfAbsent( a.getB.name, DoubleHolder::new).add(a.value)

Полагаю содержимое DoubleHolder вопросов не вызывет

Deleted
()
Последнее исправление: Deleted (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.