6

I am currently looking at Javaslang library and I am trying to convert some of my code to Javaslang.

I currently have this bit of code which is all pure Java

Cell[][] maze; //from input
Map<Cell, Long> cellCounts = Stream.of(maze)
            .flatMap(Stream::of)
            .collect(groupingBy(c -> c, counting()));

I was looking at converting this to Javaslang, as I am interested in the library and I just wanted to play around with it.

I am trying to do a similar thing, but convert to a Javaslang map instead of the java.util.Map.

I have tried this so far but then I am getting stuck as I cant see a way of converting it.

Array.of(maze)
     .flatMap(Array::of)

So I have my list of Cell objects, but I am trying to figure out how to convert this to a javaslang.collection.Map

*EDIT *

I have looked at getting my original java.util.Map to a javaslang.collections.Hashmap by this

HashMap<Cell, Long> cellsCount = HashMap.ofEntries(cellCounts
                                                        .entrySet()
                                                        .toArray(new Map.Entry[0]));

This still doesnt seem to be in line with Javaslang, but I will keep looking.

Ash
  • 2,562
  • 11
  • 31
  • is there any implementation of `javaslang.collection.Map` that would extend `java.util.Map`? – Eugene Mar 01 '17 at 15:19
  • No, the rewrote javaslang.collection.Map from the ground up. They took a functional approach to designing it and have made all the collections Immutable as well. – Ash Mar 01 '17 at 15:21
  • Inmutable collections is not a good idea all the times. It depends on the size of the collection – xiumeteo Mar 01 '17 at 23:04

3 Answers3

4

Generally grouping by has another method that takes a Map implementation you are trying to convert to, but it has to be of a type that actually extends java.util.Map:

Collectors.groupingBy(classifier, mapFactory, downstream)

From what I see javaslang.collection.Map is an interface, not an actual implementation.

Since there are no implementations of a javaslang Map that would extend java.util.Map, all you can do is create a custom collector.

Or once already collected to java.util.Map - put those into some instance of javaslang.collection.Map.

Eugene
  • 117,005
  • 15
  • 201
  • 306
  • 1
    This answer (the last part) is the correct one - by using methods like `HashMap::ofAll(java.util.Map)` one can easily put all contents of any Java `Map` into a Javaslang one. – M. Prokhorov Mar 01 '17 at 16:40
3

While answer that Eugene provided is true in general: someone has to make a custom Collector.

However, that doesn't necessarily have to be you: Javaslang guys already taken care of it for you, by providing necessary methods available on concrete implementations of Javaslang maps that will work in simplest cases. For example, on Javaslang HashMap:

javaslang.collection.HashMap::collector()

public static <K,V> Collector<Tuple2<K,V>,ArrayList<Tuple2<K,V>>,HashMap<K,V>> collector()

Returns a Collector which may be used in conjunction with Stream.collect(java.util.stream.Collector) to obtain a HashMap.

Type Parameters:

K - The key type

V - The value type

Returns:

A HashMap Collector.

Edit: However, it doesn't look like that will necessarily work, because judging from the collector's signature it:

  1. Expects a stream of Tuple<K,V>
  2. Collects all elements of that stream into an ArrayList<Tuple2<K,V>>
  3. After all elements were collected, it "finishes" by adding all contents of that ArrayList into a HashMap.

This obviously will not work, since you have Stream<Cell>, and you want to count them. However, that signature actually gives us a few things: it points out that builtin Javaslang collector does not collect directly into Javaslang Map (at least not in HashMap). Knowing this, we can look for method to convert Java Map into Javaslang map, and what we'll find will be:

javaslang.collection.HashMap::ofAll(java.util.Map)

Returns a HashMap, from a source java.util.Map.

We can add this method on top of what you have like this:

Stream<Cell> cells // <- you have this

cells.collect(
  collectingAndThen(
    groupingBy(Function.identity(), counting()),
    HashMap::ofAll
  )
);

All methods without qualifiers you can find in java.util.stream.Collectors.

This snippet will collect into normal Java Map, and as last step will call Collector's Finisher that will put all values from java Map into Javaslang map.

Note: I do not have Javaslang installed, and cannot test if above snippet compiles, but it looks like it does what you want.

Community
  • 1
  • 1
M. Prokhorov
  • 3,894
  • 25
  • 39
  • I have been looking at this, but to get it to work, do you have to convert your current stream to `Tuple2`? and have already counted the values? – Ash Mar 01 '17 at 16:03
  • 1
    Oh, sorry, I think I messed up a little. From the collector signature it does look like it collects `Tuple2` into an `ArrayList`, and then just adds all those into `HashMap`. This will not work for you, but we can sort of mimic this bevavior by using other builtin methods in a way that will work. I will keep initial version, but will update with my version of code. – M. Prokhorov Mar 01 '17 at 16:07
  • 1
    @Ash, would this new variant work for you? Do you need more info about it? – M. Prokhorov Mar 01 '17 at 16:25
  • Seems to be perfect, I didnt realize there was a `collectingAndThen()` – Ash Mar 01 '17 at 16:33
2

Disclaimer: I'm the author of Javaslang.

This simplest way to achieve it with Javaslang is the following:

    Map<Cell, Integer> cellCounts = Array.of(maze)
            .flatMap(Array::of)
            .groupBy(c -> c)
            .mapValues(Array::length);

Here Array and Map are imported from javaslang.collection.

Please note that you can substitute Array with any other Seq or Set implementation.

Daniel Dietrich
  • 2,262
  • 20
  • 25