Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Char Array To Map

Looking for a way to convert String to a map of "char" => int (index of). Obviously this assumes String has unique characters.

Is there a way to do it with Streams? I thought this would work but am running into errors trying to compile it:

String alphabet = "0123456789";
alphabet.chars().collect(Collectors.toMap(alphabet::charAt, Function.identity()));

Basically I want something equivalent to the Python code:

{c, i for i,c in enumerate("0123456789")}
like image 447
aamit915 Avatar asked Dec 03 '22 17:12

aamit915


2 Answers

This works.

Map<Integer, Character> map =
  IntStream.range(0, alphabet.length()).boxed()
           .collect(Collectors.toMap(x -> x, alphabet::charAt));

edit:

of course if you used Scala instead (also runs on the JVM) you could write it much shorter. Then it would just be alphabet.zipWithIndex.toMap

like image 94
SpiderPig Avatar answered Dec 15 '22 02:12

SpiderPig


You asked in comments how to do it with 3-argument IntStream#collect. Here it is:

Map<Character, Integer> map = IntStream.range(0, alphabet.length())
    .collect(HashMap::new, (m, i) -> m.put(alphabet.charAt(i), i), Map::putAll);

If you want to do duplicate check like toMap does and still use the 3-argument form, you will have to update both 2nd (accumulator) and 3rd (combiner) arguments to do the check:

map = IntStream.range(0, alphabet.length())
    .collect(
        HashMap::new, 
        (m, i) -> m.merge(alphabet.charAt(i), i, 
            (a,b) -> {throw new IllegalStateException("duplicate!");}
        ), 
        (m1, m2) -> m2.forEach((c,i) -> 
            m1.merge(c, i, (a,b) -> {throw new IllegalStateException("duplicate!");})
        )
    );

As you see, this gets ugly and you would probably want to define a helper method to simplify this. If you are willing to disallow parallel stream, you can get away with only checking for duplicates in accumulator but then you will have to have the combiner throw an exception:

map = IntStream.range(0, alphabet.length())
    .collect(
            HashMap::new, 
            (m, i) -> m.merge(alphabet.charAt(i), i, 
                (a,b) -> {throw new IllegalStateException("duplicate!");}
            ), 
            (m1, m2) -> {throw new AssertionError("parallel not allowed");}
);

Unless you are doing this as an exercise, it's better to just leave all this mess for toMap to deal with. That's what it's there for.

like image 29
Misha Avatar answered Dec 15 '22 03:12

Misha