1

I get a Map object:

Map<String, Shop>> shopData = GET_SHOP_DATA();

The Shop class consists of two fields:

public class Shop{
  private String name;
  int salesPerDay

  ...
}

What is the efficient way to get the summation of all shops' salesPerDay ? (I know I could iterate over the Map object & make the summation) , I mean without looping.

I know I could get a Collection object by shopData.values(). Any possibility to get the sum from there without looping?

Mellon
  • 37,586
  • 78
  • 186
  • 264
  • 1
    No way, unless you compute the sum on the fly, i.e., update it whenever the map changes or any contained item changes. That's a lot of work and pretty error-prone. – maaartinus Nov 18 '13 at 10:15
  • If guess that there are many more efficient ways to do this, depending on what "efficient" refers to. If you like to do it fast on a multi core architecture use a prarallel map-reduce. – Christian Fries Nov 18 '13 at 16:29

3 Answers3

2

The modern way to perform this operation is called map-reduce. It is part of Java 8 where you can do it in just one line but there are also frameworks which provide this. Search for "map reduce" here.

W.r.t. map-reduce you may find this post useful: Simple Java Map/Reduce framework

Java 8

In Java 8 a solution would look like this (one line!)

shopData.values().stream().map(s -> s.saledPerDay).sum();

Here

  • shopData.values() generates a collection of Shop object
  • stream() makes it a stream of Shop objects
  • map(s -> s.saledPerDay) makes it a stream of integers
  • sum() sums it up.

Since you were asking for "efficiency". It is possible to do this in parallel (using the power of a multi-core machine) and its just

shopData.values().parallelStream().map(s -> s.saledPerDay).sum();

This is fast and concise.

Java 6/7

If you like to do it in Java 6/7 then you might use a library. For example, jedi provides a reduce and the following example is actually more or less what you need: http://jedi.codehaus.org/javadoc/jedi/functional/FunctionalPrimitives.html#reduce%28java.lang.Iterable,%20jedi.functional.Functor2%29

Community
  • 1
  • 1
Christian Fries
  • 16,175
  • 10
  • 56
  • 67
1

You could wrap the Map and add your extra functionality to certain methods. Here I define a SummingMap that could be a good start. I have left out many of the methods but they would just need to forward the call to the underlying map.

interface Summable {
  public long getValue();

}

public static class Shop implements Summable {
  private String name;
  private long salesPerDay;

  @Override
  public long getValue() {
    return salesPerDay;
  }

}

public static class SummingMap<K, V extends Summable> implements Map<K, V> {
  private final Map<K, V> map;
  private long sum = 0;

  public SummingMap(Map<K, V> map) {
    this.map = map;
  }

  @Override
  public V put(K key, V value) {
    putting(key, value);
    return map.put(key, value);
  }

  @Override
  public void putAll(Map<? extends K, ? extends V> m) {
    for ( Map.Entry<? extends K, ? extends V> e : m.entrySet() ) {
      putting(e.getKey(), e.getValue());
    }
    map.putAll(m);
  }

  private void putting(K key, V value) {
    if (map.containsKey(key)) {
      sum -= map.get(key).getValue();
    }
    sum += value.getValue();
  }

  @Override
  public V remove(Object key) {
    Object o = map.get(key);
    if (o != null && o instanceof Summable) {
      sum -= ((Summable) o).getValue();
    }
    return map.remove(key);
  }

  // Other methods - just forward the call directly to the held map.

}

As @Christian correctly points out, this solution will not maintain the correct sums if you change the salesPerDay value in the Store objects without first removing them from the map.

You could enhance this solution by wrapping each object in a proxy but this again would not provide a guaranteed sum.

Making the the salesPerDay field final may help too but nothing will achieve the guaranteed correctness of adding them all up on the fly at the time the total is needed.

OldCurmudgeon
  • 64,482
  • 16
  • 119
  • 213
  • 1
    This is a dangerous solution. A map holds a reference to its object. Since his member salesPerDay is public, there is no way that the sum is updated once an element of the map is changed. If you like to implement it on the map level you need to create a kind of listener and notifications. – Christian Fries Nov 18 '13 at 13:03
0

first you need get keyset of your map. then you get every object of your Shop using iterator. then you add easily in total. try this..

     Set<String> key = shopData.keySet();
     Iterator<String> iterator = key.iterator();
     int total;
     while(iterator.hasNext()){
         Shop shop = shopData.get(iterator.next());
         total+= shop.salesPerDay;
     }
     System.out.println(total);
subash
  • 3,116
  • 3
  • 18
  • 22