3

To understand the map function in Streams better I was trying something like this:

String inputString="1+3+5";
Stream.of(inputString.split("\\+")).map(
    eachStringLiteral -> {
        output += mapOfStringAndNumber.get(eachStringLiteral) + literal;
    }
);

Where inputString is:

String inputString = "1+3+5";

however, the compiler complains and I don't know why:

The method map(Function) in the type Stream is not applicable for the arguments (( eachStringLiteral) -> {})

I also need some help in order to understand the syntax in

Function<? super String,? extends R>.

Update

This is whole code illustrating what I am trying to achieve:

HashMap<String,Double> mapOfStringAndNumber=new HashMap<String,Double>();

        mapOfStringAndNumber.put("1",270.5);
        mapOfStringAndNumber.put("2",377.5);
        mapOfStringAndNumber.put("3",377.5);
        String inputString="1+3+5";
        String literal="+";
       String output;
      java.util.stream.Stream.of(inputString.split("+")).map(eachStringLiteral->
      output+=mapOfStringAndNumber.get(eachStringLiteral)+literal
Nick Vanderhoven
  • 3,018
  • 18
  • 27
lesnar
  • 2,400
  • 7
  • 41
  • 72

4 Answers4

2

Assuming that you provide a Double value to each input String in your map mapOfStringAndNumber, you could use Pattern#splitAsStream(CharSequence input) to split your input String directly as a Stream and use Collectors.joining(CharSequence delimiter) to build your output with + as delimiter, so your final code could be:

Map<String,Double> mapOfStringAndNumber = new HashMap<>();
mapOfStringAndNumber.put("1",270.5);
mapOfStringAndNumber.put("3",377.5);
mapOfStringAndNumber.put("5",377.5);
String inputString = "1+3+5";
String output = Pattern.compile("\\+")
    .splitAsStream(inputString)
    .map(mapOfStringAndNumber::get)
    .map(d -> Double.toString(d))
    .collect(Collectors.joining("+"));

System.out.println(output);

Output:

270.5+377.5+377.5
Nicolas Filotto
  • 43,537
  • 11
  • 94
  • 122
  • `Stream.of(inputString.split("\\+")).forEach( eachStringLiteral -> { output += mapOfStringAndNumber.get(eachStringLiteral) + literal; } );` won't work, because variables used in lambda expressions should be final or effectively final. – Shem Dec 02 '16 at 12:16
  • 1
    @Shem you assume that they are **local** variables which is not true here otherwise the OP would have this compilation error too – Nicolas Filotto Dec 02 '16 at 12:19
  • @NicolasFilotto thanks for your post , its helpful +1 . but as Shem mentioned after removing the {} i have this "local variable must be final" .I know what this error means but i am not sure how can i have the intermediate results. – lesnar Dec 02 '16 at 12:32
  • 1
    If it is local after all, you cannot modify it inside a lambda. See my answer on how to do this fully functional. – Shem Dec 02 '16 at 12:35
2

map, by definition, takes an input stream and applies a Function to each element of this stream, creating a new stream out of those modified elements.

So the count of items in input stream has to be the same as the count of items in output stream.

The error you are mentioning occurs, because your mapping function does not return anything: output+=mapOfStringAndNumber.get(eachStringLiteral)+literal only modifies the output variable and returns nothing.

From what I'm seeing here, you want to perform map and reduce operations:

  • map operation changes each element of input stream into its value from mapOfStringAndNumber and adding literal to it
  • reduce operation, in your case, is summing up all elements of your stream

To achieve this using Java 8 features, you need to first map the elements of your stream and then sum them up. You can do it like this:

String sum = Stream.of(inputString.split("\\+"))
       .map(stringLiteral->
          mapOfStringAndNumber.get(stringLiteral).toString())
       //join all the strings in the stream with a provided delimiter
       .collect(Collectors.joining(literal));
Shem
  • 565
  • 2
  • 10
2

I have a couple of suggestions for your code:

  1. If you use a {}-block and you want to create a Function (not a Runnable, Consumer or similar) you have to return something.
  2. Unless you have a very special case, use Integer.parseInt(String) instead of a Map of Strings to Integers
  3. You must not modify output from within a Stream. That could lead to concurrancy issues.
  4. Do you really need literal?

My example code for your usecase would look like this:

String inputString="1+3+5";
return

        // split input and create stream
        Stream.of(inputString.split("\\+"))

        // convert each item to Integer
        .mapToInt(Integer::parseInt)

        // sum up everything
        .sum();
slartidan
  • 20,403
  • 15
  • 83
  • 131
1

output+=mapOfStringAndNumber.get(eachStringLiteral)+literal; is an expression. Lambda expressions that have a body consisting of a single expression should not have them placed within curly braces:

Stream.of(inputString.split("+")).map(eachStringLiteral -> 
  output+=mapOfStringAndNumber.get(eachStringLiteral)+literal;
);

See the section of the Java Language Specification concerning lambda bodies:

A lambda body is either a single expression or a block (§14.2)...

LambdaBody:
Expression
Block

Alternatively you can just add a return clause before the output variable to make it a statement.

Community
  • 1
  • 1
M A
  • 71,713
  • 13
  • 134
  • 174