11

I am new to lambda expressions and am trying to use them to reduce the following code to the lambda equivalent. I have looked into reduce and flatMap and forEach, as well as several other things, but I am obviously missing something because everything that I try is either syntactically incorrect or I don't have a reference for what I need.

I need to perform an analysis of each element against all other elements in a collection. I coded that as nested loops with a conditional. Once non-matching elements have been identified, a computation is done using both elements. Finally, I want a collection of results for each comparative computation.

So, here's the original code:

final List<Element> updated = new ArrayList<>(elements.size());

for (final Element first : elements) {
    Attribute newAttribute = first.getAttribute();

    for (final Element second : elements) {
        if (!first.equals(second)) {
            newAttribute = newAttribute.add(computeChange(first, second));
        }
    }
    final Element newElement = new Element(first.getEntry(), newAttribute, first.getValue());
    updated.add(newElement);
}

Then, I tried many variations of lambda expressions, the simplest of which is:

elements.parallelStream()
         .map(first -> new Element(first.getEntry(), first.getAttribute().add(
         computeChange(first, second)), first
         .getValue())).collect(Collectors.toList()));

Obviously, this is wrong as there is no reference to second available to me and no condition/filter for second being not equal to first.

How do I reduce this nested loop with conditional returning a collection to a lambda expression?

Any help here is greatly appreciated.

7
  • 3
    This is tough because your newAttribute = newAttribute.add(...) updates aren't parallelizable. This would be easier if you could aggregate all of the computeChange results and then create an Attribute (or Element) from that aggregate. Commented Apr 28, 2015 at 20:13
  • I would just leave it as it is. Commented Apr 28, 2015 at 20:18
  • What does computeChange return? An Element, Attribute, or a number? Commented Apr 28, 2015 at 20:45
  • Not 100% sure, but I think what you are trying to do is not possible, at least not with parallel streams. You'd have to use reduce with three parameters, as you are trying to accumulate Attributes and Elements, but then you'll need a combiner for combining the results of the different parallel streams, combining two Attributes to a new one. So: How would you combine two Attributes? Commented Apr 28, 2015 at 21:02
  • computeChange returns an Attribute Commented Apr 28, 2015 at 22:52

1 Answer 1

3

Try:

elements.stream()
    .map(first -> {
       Attribute newAttribute = elements.stream().filter(second -> !first.equals(second))
                .map(second -> computeChange(first, second))
                .reduce(first.getAttribute(), (a, b) -> a.add(b))
                return new Element(first.getEntry(), newAttribute, first.getValue());
            }).collect(Collectors.toList()));
Sign up to request clarification or add additional context in comments.

5 Comments

I don't think this .foreach(newAttribute::add); will work, as add seems to create a new attribute. Maybe reduce will work, though.
@tobias_k the foreach reports the error: The method foreach(Attribute::add) is undefined for the type Stream<Attribute>. Using reduce eliminates the error.
pgerstoft, @tobias_k I needed to change the argument of the foreach/reduce to be Attribute:add otherwise I received the error "The type Attribute does not define add(Attribute, Attribute) that is applicable here". However, I am not passing unit tests, so I don't think that the repeated update/add is working properly. I am going to keep poking at it, but if you have any suggestions, I would love to hear them.
so, it appears that using foreach produced misleading errors, but forEach allowed the arguments as provided by pgerstoft. However, it does not produce the desired results. I'm still looking.
@pgerstoft it almost worked, there appears to be an off by one error. Thanks for all the help. When I update the code as follow, I get it to work: elements.stream() .map(first -> { Attribute zero = new Attribute(); Attribute newAttribute = elements.stream().filter(second -> first != second).map(second -> computeChange(first, second)).reduce(zero, (a, b) -> a.add(b)); return new Element(first.getEntry(), first.getAttribute().add(newAttribute), first.getValue()); }).collect(Collectors.toList()))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.