stevenhz
stevenhz

Reputation: 57

java reduce on the array in the class

I try to use reduce to sum the HashMap value.

public class Link 
{
    private double[] flows;
    public Link(double[] f) {
        setFlows(f);
    }
    public double[] getFlows() {
        return flows;
    }
    public void setFlows(double[] flows) {
        this.flows = flows;
    }

    public static void main(String argv[])
    {
         // TEST for reduce on HashMap
         HashMap<Integer, Link> id1 = new HashMap<Integer, Link>();
         id1.put(1, new Link(new double[]{10,1,30}));
         id1.put(2, new Link(new double[]{20,2,3}));
         id1.put(3, new Link(new double[]{30,2,3}));
         id1.put(4, new Link(new double[]{40,2,30}));

         double[] my_sum = new double[3];
         for (int i=0; i < 3; ++i)
         {
              my_sum[i] = id1.entrySet().stream().mapToDouble(e->e.getValue().getFlows()[i]).sum();
         }
         assert(my_sum[0] == 100);
         assert(my_sum[1] == 7);
         assert(my_sum[2] == 66);
    }
}

In the for loop, I want to sum the value of each array item in the Link class. However, I have the problem with:

local variable defined in enclosing scope must be final or effectively final

Basically I don't want to define a final variable as class member, How to solve it?

Or is there a better to sum the value? (without the for loop?)

Upvotes: 1

Views: 1453

Answers (3)

Tagir Valeev
Tagir Valeev

Reputation: 100279

An alternative which looks better to me is to use Arrays.setAll:

double[] my_sum = new double[3];
Arrays.setAll(my_sum, 
  i -> id1.entrySet().stream().mapToDouble(e->e.getValue().getFlows()[i]).sum());

Here i is effectively final.

Upvotes: 2

OldCurmudgeon
OldCurmudgeon

Reputation: 65859

If you add an add method (making a Link mutable unfortunately) you can use a Link for the sum too.

    // Makes it mutable - will try immutable later
    public void add(Link other) {
        for (int i = 0; i < flows.length; i++) {
            flows[i] += other.flows[i];
        }
    }

    public static void main(String argv[]) {
        // TEST for reduce on HashMap
        HashMap<Integer, Link> id1 = new HashMap<>();
        id1.put(1, new Link(new double[]{10, 1, 30}));
        id1.put(2, new Link(new double[]{20, 2, 3}));
        id1.put(3, new Link(new double[]{30, 2, 3}));
        id1.put(4, new Link(new double[]{40, 2, 30}));

        Link sum = new Link(new double[3]);
        id1.entrySet().stream().forEach(l -> sum.add(l.getValue()));
    }

Upvotes: 0

Tunaki
Tunaki

Reputation: 137209

You're going at it the wrong way: you shouldn't have an external for loop but handle everything inside a Stream pipeline.

You could make it work by storing the variable i into a final local variable.

In the following code, only the values of the map are kept (since we're not interested in the keys). Then each value is mapped to their flows. Finally, the resulting Stream<double[]> is reduced by first creating an array of 3 elements (initialized to 0) and then combining two double arrays into a resulting one by summing the value at the same index.

We have to rely on using a Stream over the indexes for that because there is no built-in facility for zipping two Streams together.

double[] my_sum = 
    id1.values().stream()
                .map(Link::getFlows)
                .reduce(
                    new double[3],
                    (v1, v2) -> IntStream.range(0, v2.length).mapToDouble(i -> v1[i] + v2[i]).toArray()
                );

System.out.println(Arrays.toString(my_sum)); // prints [100.0, 7.0, 66.0]

Upvotes: 3

Related Questions