简体   繁体   中英

Java Lambda Multiple operations on a single stream

Scenario: I have a object with 2 functions -->

<Integer> getA(); <Integer> getB();

I have a list of objects, say List<MyObject> myObject .

Objective Iterate over the List and get sum of A and B's in the List of object.

My Solution

myObject.stream().map(a -> a.getA()).collect(Collectors.summingDouble());

myObject.stream().map(a -> a.getB()).collect(Collectors.summingDouble());

The Question: How can I do both of these at the same time? This way I will not have to iterate twice over the original list.

@Edit: I was doing this because some of the filters that I used were of O(n^3). Kind of really bad to do those twice.

Benchmark : It really matters if it is T or 2T when the program runs for half hour on an i5. This was on much lesser data and if I run on a cluster, my data would be larger too.

It does matter if you can do these in one line!.

You need to write another class to store the total values like this:

public class Total {
    private int totalA = 0;
    private int totalB = 0;

    public void addA(int a) {
        totalA += a;
    }

    public void addB(int b) {
        totalB += b;
    }

    public int getTotalA() {
        return totalA;
    }

    public int getTotalB() {
        return totalB;
    }
}

And then collect the values using

Total total = objects.stream()
        .map(o -> (A) o)
        .collect(Total::new,
                (t, a) -> {
                    t.addA(a.getA());
                    t.addB(a.getB());
                },
                (t1, t2) -> { });
//check total.getTotalA() and total.getTotalB()

You can also use AbstractMap.SimpleEntry<Integer, Integer> to replace Total to avoid writing a new class, but it's still kind of weird because A/B are not in a key-value relationship.

The most optimal probably still would be a for loop. Though the stream alternative has parallelism as option, and is likely to become as efficient soon. Combining two loops to one is not necessarily faster.

One improvement would be to use int instead of Integer .

List<String> list = Arrays.asList("tom","terry","john","kevin","steve");
int n = list.stream().collect(Collectors.summingInt(String::length));
int h = list.stream().collect(Collectors.summingInt(String::hashCode));

I favour this solution.

If one would make one loop, there are two alternatives:

  • putting both ints in their own class. You might abuse java.awt.Point class with int x and int y.
  • putting both ints in a long. When no overflow, then one can even sum in the loop on the long.

The latter:

List<String> list = Arrays.asList("tom","terry","john","kevin","steve");
long nh = list.stream()
    .collect(Collectors.summingLong((s) -> (s.hashCode() << 32) | s.length()));
    int n = (int) nh;
    int h = (int) (nh >> 32L);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM