lesnar
lesnar

Reputation: 2480

Streams : how map in Streams work

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

Upvotes: 3

Views: 233

Answers (4)

Nicolas Filotto
Nicolas Filotto

Reputation: 44965

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

Upvotes: 2

Shem
Shem

Reputation: 565

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));

Upvotes: 2

M A
M A

Reputation: 72854

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.

Upvotes: 1

slartidan
slartidan

Reputation: 21576

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();

Upvotes: 2

Related Questions