2

I want to convert a list to a map where the key is just a counter and it needs to adhere to the order of the list. I currently have this code:

private static Map<String, String> convertListToMap(final List<String> list) {
    AtomicInteger counter = new AtomicInteger(0);
    Map<String, String> map = list.stream().collect(Collectors.toMap((c) -> {
        Integer integer = counter.incrementAndGet();
        return integer.toString();
    }, (c) -> c));

    return map;
}

I have two questions:

  1. In a simple console app test on my desktop, the counter is preserving the order of the list. Can we be sure the order will always be preserved when executed anywhere else?
  2. Is there a better way to code this?
Romonov
  • 8,145
  • 14
  • 43
  • 55
  • Question #1 is essentially asking the same thing as https://stackoverflow.com/questions/29216588/how-to-ensure-order-of-processing-in-java8-streams – dnault Feb 26 '20 at 22:11
  • I edited the 2nd question to be more generic instead of asking for a solution that is not using AtomicInteger. Sorry for the confusion. – Romonov Feb 26 '20 at 22:20

3 Answers3

5

Try it this way.

static Map<String, String> convert(List<String> list) {
    return IntStream.range(0, list.size()).boxed()
            .collect(Collectors.toMap(n -> String.valueOf(n+1), list::get,
                    (a, b) -> a, LinkedHashMap::new));
}

Notes:

  • The Merge function (a, b) -> a is not really contributing to this.
  • The supplier of LinkedHashMap::new ensures order is retained. Unfortunately, there is not a Collector.toMap that permits a Supplier without the merge function.
Naman
  • 27,789
  • 26
  • 218
  • 353
WJS
  • 36,363
  • 4
  • 24
  • 39
  • Thanks. Is this better than using an AtomicInteger? Or are both approaches equally good and it is just a matter of preference? – Romonov Feb 26 '20 at 23:17
  • AtomicInteger is primarily used for situations when you have threads competing for a unique value between them. Unless you are using threads (and perhaps even then) you should not have to about this. And Atomic* methods do incur some overhead in the way they ensure thread safety. – WJS Feb 27 '20 at 00:40
  • @Naman Nope. I like it. I've got to remember to use method references when applicable. They just don't always occur to me. Thanks!! – WJS Feb 27 '20 at 02:21
  • 1
    @Naman Actually I had to change things. The OP initialized `AtomicInteger` to 0 but used incrementAndGet so it became 1. Thus I presumed the first number must be a 1. But I still need 0 in my stream to get the first value of the list. And the `rangeClosed` method makes the last index too long. – WJS Feb 27 '20 at 02:32
  • @WJS Makes sense, the index to get and one stored in the Map differs, causing the confusion there. – Naman Feb 27 '20 at 02:44
3

Probably you can use IntStream to map index as key to value, and use LinkedHashMap for preserving order

    IntStream.range(0, list.size())
             .mapToObj(i -> new AbstractMap.SimpleEntry<>(String.valueOf(i+1), list.get(i)))
             .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> 1, LinkedHashMap::new));
Ryuzaki L
  • 37,302
  • 12
  • 68
  • 98
  • return type `Map` would be incompatible with the method signature unless `String.valueOf` is used. – Naman Feb 27 '20 at 02:17
  • `map` operation would make it incorrect since you cannot `get` from the `list` anymore (the `new AbstractMap.SimpleEntry<>(i, 10)` part). It might move very close to the [another answer](https://stackoverflow.com/a/60423066/1746118) I've edited but you don't really need to map to `SimpleEntry` if in the next step you would collect it `toMap`. – Naman Feb 27 '20 at 02:30
0

Regarding your first question: As long as your number will not change, your order would be preserved. A better solution would be a LinkedList (https://docs.oracle.com/javase/7/docs/api/java/util/LinkedList.html) where entries are ordered by the sequence you add them (It may be easier, I do not know your application).

Regarding your second question: The AtomicInteger mainly advances due to better thread safety (Performance Difference of AtomicInteger vs Integer). If you are not performing any concurrent operations there should be no noticable difference. Therefore one could use a normal Integer.

Vincentklw
  • 61
  • 1
  • 6
  • Local variables referenced by a lambda must be effectively final. How would a normal integer work? – dnault Feb 26 '20 at 22:08
  • All correctly implemented `List` implementations preserve insertion order, not just `LinkedList`. – dnault Feb 26 '20 at 22:13