There is a class with fields of value and quantity:

class Order { private int prise; private int volume; Order(int prise, int volume) { this.prise = prise; this.volume = volume; } // и геттеры... } 

And the collection that contains these elements: List<Order> orders = new LinkedList<>();

The collection often contains items that have the same prise . I need to write an algorithm for working with a collection that would combine all Order instances with the same prise , and their volume would be added together.
That is, according to the results of the program's operation, Order objects with the same prise should not remain, and the volume field should contain a value equal to the sum of all the volume that the duplicates had deleted.

But the essential point is that there are several thousand elements in the collection, and speed in the context of my task is of great importance.

By the way, is it necessary to use LinkedList for data storage here?

  • Regarding the used English words: the price is written as price , and quantity and amount are much more suitable for quantity . - Regent

3 answers 3

Option using Java 8 Stream API "at maximum":

 List<Order> groupedOrders = orders.stream() .collect(Collectors.groupingBy(Order::getPrise, Collectors.summingInt(Order::getVolume))) .entrySet().stream() .map(e -> new Order(e.getKey(), e.getValue())) .collect(Collectors.toList()); 

It is assumed that there are no setters in the Order , so a new list is created from the new Order .

Tested on an ArrayList of a million Order objects:

 List<Order> orders = IntStream.range(0, 1000 * 1000) .mapToObj(i -> new Order(i % 100, i)) .collect(Collectors.toList()); 

Runtime was around 40-45ms. So I think it’s not worth worrying about the speed of several thousand elements.


Comparing ArrayList with LinkedList can be found in this question . As stated in the accepted answer in this question: "if you are not sure, start with ArrayList ".
And in any case, you will hardly notice at least some difference between them on just a few thousand elements.

  • A cool option, but please tell me if the setters have it will work faster? - Pavel
  • one
    @Pavel specifically this code is "sharpened" for creating a new list with new Order . To reuse the existing list and existing Order will have to redo all the code. If you look not at the code, but at the very idea of ​​re-use (due to the existence of setters), then I think you can achieve a slight acceleration. But, again, I repeat, the difference will be felt only under a really solid load. - Regent
  • one
    @Paul yes, so that the length of the list is at least hundreds of thousands of elements. Or that this code is called hundreds of times per second. In general, the main thing is how much time it takes to execute a specific code on a specific computer. Agree: if in both cases it takes less than one millisecond to execute the code, then you should not bother with optimizing this code. - Regent
  • one
    @Pavel What's the difference in this case? The code itself will work well with float prise . - Regent
  • one
    e.getKey() returns the price, e.getValue() is the total quantity for the given price. In the second line there is a grouping by price, and the results fall into the Map , from which in the fourth line these results are obtained in pairs "key-value". - Regent

Ideal if you have a set method for volume:

 static Collection<Order> mergeOrdersVolumeSet(Collection<Order> orders){ //Мапа на основе которой будем объединять. Ключ - price final Map<Integer,Order> orderMap = new HashMap<>(); //Проходим по старым ордерам orders.forEach(order -> { //Пытаемся по прайсу ордера найти уже существующий final Order o = orderMap.get(order.getPrice()); if (o==null){//Если не нашли - добавляем orderMap.put(order.getPrice(),order); }else {//Иначе, в уже существующем меняем volume o.setVolume(o.getVolume()+order.getVolume()); } }); return orderMap.values(); } 

The remaining options make sense if you do not have a volume setter.

You can do without mapy, but I'm not sure that this will be faster:

 static Collection<Order> mergeOrders(Collection<Order> orders){ final Collection<Order> result = new ArrayList<>(); //Пока старая коллекция не пуста while (!orders.isEmpty()){ //Использую итератор, т.к. буду удалять из изначальной коллекции. final Iterator<Order> oldOrdersIterator = orders.iterator(); //Получаем первый Ордер final Order firstOrder = oldOrdersIterator.next(); //Одтельно получаем значение кол-ва int volume = firstOrder.getVolume(); //Сразу удаляем его из коллекции oldOrdersIterator.remove(); //Проходим по остальным значениям старой коллекции, если они еще есть while (oldOrdersIterator.hasNext()){ //Получаем текущее final Order oldOrder = oldOrdersIterator.next(); //Если прайс совпадает с первым ордером if (firstOrder.getPrice()==oldOrder.getPrice()){ oldOrdersIterator.remove();//Удаляем его из коллекции volume+=oldOrder.getVolume();//Обновляем актуальное значение кол-ва } } //Если значение кол-ва отличается от начального if (volume!=firstOrder.getVolume()){ //Создаем новый ордер на основе старого прайса и нового кол-ва result.add(new Order(firstOrder.getPrice(),volume)); }else { //Иначе добавляем неизменившийся result.add(firstOrder); } } return result; } 

Option with a map:

 static Collection<Order> mergeOrdersMap(Collection<Order> orders){ //Можно вместо коллекции ордеров использовать Integer значение, равное сумме volumes, но тут не ясно что быстрее. // Т.к. в такой ситуации придется заново инициализировать каждый ордер Map<Integer,Collection<Order>> mergeMap = new HashMap<>(); //Проходим по всем старым ордерам orders.forEach(order -> { //Пытаемся получить коллекцию ордеров с текущим значением прайса Collection<Order> keyOrders = mergeMap.get(order.getPrice()); if (keyOrders==null){//Если не получили //то кидаем в мапу новый прайс, и с ним ассоциируем новый лист, в котором пока один ордер mergeMap.put(order.getPrice(),new ArrayList<>(Collections.singleton(order))); }else{ //Иначе добавляем новый ордер keyOrders.add(order); } }); //Создаем результирующую коллекцию. Сразу задаем размер как у получившейся мапы. //Т.к. кол-во прайсов и есть кол-во ордеров после объединения final Collection<Order> result = new ArrayList<>(mergeMap.size()); //Проходим по мапе mergeMap.forEach((integer, orders1) -> { //Если размер списка == 1, то значит похожих ордеров не было, и можно добавить прямо этот if (orders1.size()==1){ result.add(orders1.stream().findFirst().get()); }else { //Иначе проходим по коллекции, считаем суммарный volume int vol = 0; for (Order o: orders1){ vol+=o.getVolume(); } //И добавляем новый объект result.add(new Order(integer,vol)); } }); return result; } 

Check (The result is the same everywhere):

 public static void main(String[] args) { Collection<Order> orders = new ArrayList<>(); orders.add(new Order(1,1)); orders.add(new Order(2,2)); orders.add(new Order(3,3)); orders.add(new Order(4,4)); orders.add(new Order(1,2)); orders.add(new Order(2,5)); orders = mergeOrders(orders); orders.forEach(order -> System.out.println("Order -> price: "+order.getPrice()+"; volume: "+order.getVolume())); } 

Result:

Order -> price: 1; volume: 3

Order -> price: 2; volume: 7

Order -> price: 3; volume: 3

Order -> price: 4; volume: 4

    Take HashMap<Integer,Order> , and add one at a time ( price - key), before adding, check if an element with such a key has already been added, change its volume .