2

I have been trying to work with collect method of Stream, to convert list into HashMap. I am using below code as reference :

String result = list.parallelStream().collect(StringBuilder::new,
            (response, element) -> response.append(" ").append(element),
            (response1, response2) -> response1.append(",").append(response2.toString()))
            .toString();

while I write the below incomplete statement in eclipse and do a ctrl+space at ?

Map<String,Choice> map1=  list1.stream().collect(()-> new HashMap<String,Choice>(), 
        (r,s) -> r.?,(r,s) -> r.putAll(s));

considering the code snippet that uses StringBuilder, my expectation was that, the first argument of accumulator function should be a HashMap as I have used HashMap::new as supplier function. As per this understanding, eclipse should suggest me methods of HashMap, but that's not the case.

However, this seems to work fine

list1.stream().collect(ArrayList::new, ArrayList::add, ArrayList::addAll);

Already referred Java 8 Int Stream collect with StringBuilder but, not much luck with this.

Kohei TAMURA
  • 4,970
  • 7
  • 25
  • 49
Rajeev Akotkar
  • 1,377
  • 4
  • 26
  • 46
  • 4
    Please read [Why not to upload images of code on SO when asking a question?](https://meta.stackoverflow.com/questions/285551/why-not-to-upload-images-of-code-on-so-when-asking-a-question), then edit your question accordingly. – Joe C Jul 14 '17 at 20:30
  • removed the image as per instructions – Rajeev Akotkar Jul 14 '17 at 20:48
  • 2
    Do you understand your own question actually? What does list contain? What do you want to obtain? – Eugene Jul 14 '17 at 22:01
  • Explain what is your input and what you expect as output rather than explaining your code. – user3734782 Jul 15 '17 at 06:30
  • my input is a list of Choice Object (int id,String string) and I want to convert that into a map having string as key and object as value – Rajeev Akotkar Jul 15 '17 at 06:31
  • What’s your question about, Eclipse’s code completion or how to solve this Stream collect task? – Holger Jul 17 '17 at 07:47
  • initially I was not getting code completion , methods of hasmap were not showing up,but later on ode got compiled with eclipse.Also,I wanted to follow this particular approach to collect (Stream) – Rajeev Akotkar Jul 17 '17 at 09:10

4 Answers4

4

If you wanted to explore more the supplier, accumulator, combiner you should have written that a bit more clear to begin with.

Suppose you have a class like this:

static class Foo {
    private final int id;

    private final String s;

    public Foo(int id, String s) {
        super();
        this.id = id;
        this.s = s;
    }

    public int getId() {
        return id;
    }

    public String getS() {
        return s;
    }

    @Override
    public String toString() {
        return "" + id;
    }
}

If you know that s in this case will be unique you can simply do this:

HashMap<String, Foo> result = Arrays.asList(new Foo(1, "a"), new Foo(2, "b"))
            .stream()
            .collect(HashMap::new, 
                    (map, foo) -> map.put(foo.getS(), foo), 
                    HashMap::putAll);

    System.out.println(result); // {a=1, b=2}

But if s is not unique you would probably need to collect to a List and that makes things a bit more complicated:

 HashMap<String, List<Foo>> result = Arrays.asList(
            new Foo(1, "a"),
            new Foo(2, "b"),
            new Foo(3, "a"))
            .stream()
            .parallel()
            .collect(HashMap::new,
                    (map, foo) -> {
                        map.computeIfAbsent(foo.getS(), s -> new ArrayList<>()).add(foo);
                    },
                    (left, right) -> {
                        for (HashMap.Entry<String, List<Foo>> e : right.entrySet()) {
                            left.merge(
                                    e.getKey(),
                                    e.getValue(),
                                    (l1, l2) -> {
                                        l1.addAll(l2);
                                        return l1;
                                    });
                        }
                    });

    System.out.println(result); // {a=[1, 3], b=[2]}
Eugene
  • 117,005
  • 15
  • 201
  • 306
  • 1
    Why aren't you using `map.computeIfAbsent(foo.getS(), new ArrayList<>()).add(foo)` in the accumulator? – fps Jul 16 '17 at 00:15
  • 1
    And your combiner could be written more succintly as `right.forEach((k, v) -> left.merge(k, v, (l1, l2) -> { l1.addAll(l2); return l1; }))` – fps Jul 16 '17 at 00:18
  • 2
    @FedericoPeraltaSchaffner I sort of missed that (`computeIfAbsent`) :) especially since it is present in the documentation also... thx. – Eugene Jul 16 '17 at 11:18
2

Convertting a list into a map? Collectors.toMap(..) might be what you need.

the javadocs have a couple good examples: https://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.html#toMap-java.util.function.Function-java.util.function.Function-

Andreas
  • 4,937
  • 2
  • 25
  • 35
1

Accoring to your comment, I guess you can do it as simple as that.

List<Choice> choices = new ArrayList<>();

Map<String, Integer> map = choices.stream().collect(Collectors.toMap(Choice::getString, Choice::getID);

Rather than writing you own suppliers and accumulators, I recommend using Collectors and its static methods, whose are used to transfer stream to any type of Collection.

EDIT: If value of the map is required to be Choice. Function interface has static method called identity() (type of last stream transformation before collect was called, Choice in this example).

Map<String, Choice> map = choices.stream().collect(Collectors.toMap(Choice::getString, Function.identity());
user3734782
  • 137
  • 2
  • 11
  • thats correct but as mentioned in one of the commnets that I as already aware of this solution , but was trying to explore though the options.i.e no of different possible solutions. – Rajeev Akotkar Jul 15 '17 at 07:02
  • Why are you aware? However, I am not sure if I understood your question correctly, but value of the map should be `Integer` or `Choice`? You said 'object as value', where both Integer and Choice are objects. – user3734782 Jul 15 '17 at 07:09
  • should be a choice , key of hashmap should be String and value should be a choice object. – Rajeev Akotkar Jul 15 '17 at 07:14
  • This is a fairly simple problem and I already found the solution https://stackoverflow.com/questions/20363719/java-8-listv-into-mapk-v. My only constraint was I wanted to do it a particular way. – Rajeev Akotkar Jul 15 '17 at 07:36
0
Map<String,Choice> map= list1.stream().collect(()-> new HashMap<String,Choice>(), (r,s) -> r.put(s.getString(),s),(r,s) -> r.putAll(s));
TalG
  • 677
  • 1
  • 9
  • 26
Rajeev Akotkar
  • 1,377
  • 4
  • 26
  • 46