44

How can I convert a String into a Map:

Map m = convert("A=4 H=X PO=87"); // What's convert?
System.err.println(m.getClass().getSimpleName()+m);

Expected output:

HashMap{A=4, H=X, PO=87}
Artjom B.
  • 61,146
  • 24
  • 125
  • 222
PeterMmm
  • 24,152
  • 13
  • 73
  • 111

8 Answers8

62

There is no need to reinvent the wheel. The Google Guava library provides the Splitter class.

Here's how you can use it along with some test code:

package com.sandbox;

import com.google.common.base.Splitter;
import org.junit.Test;

import java.util.Map;

import static org.junit.Assert.assertEquals;

public class SandboxTest {

    @Test
    public void testQuestionInput() {
        Map<String, String> map = splitToMap("A=4 H=X PO=87");
        assertEquals("4", map.get("A"));
        assertEquals("X", map.get("H"));
        assertEquals("87", map.get("PO"));
    }

    private Map<String, String> splitToMap(String in) {
        return Splitter.on(" ").withKeyValueSeparator("=").split(in);
    }

}
Logan
  • 53
  • 5
Daniel Kaplan
  • 62,768
  • 50
  • 234
  • 356
  • 4
    Well, it does answer the question (even including the library requirement) so .. +1. Although I'm not entirely sure the dependency is justified if *just* for this task. –  Feb 08 '13 at 08:18
  • There will be `IllegalArgumentException` when the string passed into the `splitToMap` is either an empty string `""` or a string with irregular structure like `"A=4=3"`. – ssgao Apr 17 '15 at 03:59
  • 1
    Also `IllegalArgumentException` for trailing separator, e.g. `"A=3 B=4 "` – Sarah Phillips Feb 10 '16 at 17:52
  • [Splitter.MapSplitter (Guava: Google Core Libraries for Java 19.0 API)](https://google.github.io/guava/releases/19.0/api/docs/com/google/common/base/Splitter.MapSplitter.html#split(java.lang.CharSequence)) – ycomp Aug 17 '17 at 06:05
  • Also, IndexOutOfBoundsException for when the string contains a `,`. – KumarAnkit Dec 19 '18 at 10:52
31

Java 8 to the rescue!

import static java.util.stream.Collectors.*;

Map<String, String> result = Arrays.stream(input.split(" "))
    .map(s -> s.split("="))
    .collect(Collectors.toMap(
        a -> a[0],  //key
        a -> a[1]   //value
    ));

NOTE: Assuming no duplicates. Otherwise look into the 3rd 'mergeFunction' argument.

ericdemo07
  • 461
  • 8
  • 16
Marsellus Wallace
  • 17,991
  • 25
  • 90
  • 154
  • 1
    NOTE: The code "Collectors." is not needed but my answer has been edited and approved by the community. Democracy won, I lost... – Marsellus Wallace Feb 27 '17 at 14:39
  • My IDE gives following message if the code "Collectors" is not used `The method toMap(( a) -> {}, ( a) -> {}) is undefined`, what IDE you use? @Gevorg – ericdemo07 Mar 09 '17 at 11:57
  • The one and only: IntelliJ IDEA! Are you using the 'static import' as shown in the answer? – Marsellus Wallace Mar 09 '17 at 13:26
  • 1
    I like this answer, because I was able to rewrite it in Kotlin like this: `input.split(" ").map { it.split("=") }.associate { Pair(it[0], it[1]) }`, thus rendering Guava unnecessary. – arekolek Jul 24 '17 at 10:42
  • @arekolek Hi arekolek, note that there is no Guava in the answer. It's pure Java 8. – Marsellus Wallace Jul 24 '17 at 13:47
  • 1
    @Gevorg Sorry for the confusion, I was referring to the other answer and the fact that I couldn't use Java 8, but can use Guava or Kotlin. I see I didn't make this clear in my comment. – arekolek Jul 24 '17 at 13:50
23

You don't need a library to do that. You just need to use StringTokenizer or String.split and iterate over the tokens to fill the map.

The import of the library plus its settings would be almost as big as the three lines of code needed to do it yourself. For example :

public static Map<String, String> convert(String str) {
    String[] tokens = str.split(" |=");
    Map<String, String> map = new HashMap<>();
    for (int i=0; i<tokens.length-1; ) map.put(tokens[i++], tokens[i++]);
    return map;
}

Note that in real life, the validation of the tokens and the string, highly coupled with your business requirement, would probably be more important than the code you see here.

Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
7
public static Map<String, String> splitToMap(String source, String entriesSeparator, String keyValueSeparator) {
    Map<String, String> map = new HashMap<String, String>();
    String[] entries = source.split(entriesSeparator);
    for (String entry : entries) {
        if (!TextUtils.isEmpty(entry) && entry.contains(keyValueSeparator)) {
            String[] keyValue = entry.split(keyValueSeparator);
            map.put(keyValue[0], keyValue[1]);
        }
    }
    return map;
}

And now you can use it for different types of entries/key-values separators, just like this

Map<String, String> responseMap = splitToMap(response, " ", "=");
Denys Vasylenko
  • 2,135
  • 18
  • 20
  • 1
    This worked great for me! I had a comma-delimited list so just passed `(response, ", ", "=")`. However, some of my keys had no value, so I'd get an indexOutOfBoundsException on keyValue[1]. This was fixed with `map.put(keyValue[0], (keyValue.length > 1 ? keyValue[1] : ""));` – snowmanjack Jan 28 '16 at 19:32
  • This is a good method. If your separator has a character like |, then you need to pass "\\|" to escape it instead of just "|" which will not work! – vikkee Sep 16 '16 at 07:14
4

split String by " ", then split each item by "=" and put pairs into map. Why would you need "library" for such elementary thing?

kachanov
  • 2,696
  • 2
  • 21
  • 16
1

Let me propose another way for specific case when you have key-value pairs in different lines: to use Java's built-in Properties class:

Properties props = new Properties();
props.load(new StringReader(s));

BENEFITS:

  • short
  • for any Java version
  • gives you a ready-to-use Map<Object, Object> also supplying handy String props.getProperty(String) method
  • StringReader has built-in 8k buffer (you may adjust buffering on mega input)
  • steady against different newline characters
  • you may base on defaults Map with new Properties(defaultsMap)

WARNINGS (could be virtue or vice):

  • has special parsing rules for '.properties' format (you may want to peek inside Properties class to learn more):
    • trims spaces/tabs from keys/values
    • has special-meaning characters:
      • !,# at beginning of line is comment
      • = or : is key-value separator
      • \ is used as escape character for unicode/special chars. For example, you may want to prepare your string with s = s.replace("\\", "\\\\");
  • resulting Map is based on HashTable having synchronization unneeded in most cases
Volo Myhal
  • 74
  • 5
0
private HashMap<String, String> convert(String str) {
    String[] tokens = str.split("&");
    HashMap<String, String> map = new HashMap<String, String>();
    for(int i=0;i<tokens.length;i++)
    {
        String[] strings = tokens[i].split("=");
        if(strings.length==2)
         map.put(strings[0], strings[1].replaceAll("%2C", ","));
    }

    return map;
}
Artjom B.
  • 61,146
  • 24
  • 125
  • 222
Arun Shankar
  • 2,295
  • 16
  • 20
  • 2
    Thanks for your answer. When posting code, please also try to explain the main parts of the code, so that the OP can also understand it. – Eduard Luca Aug 20 '14 at 08:57
-1
String elementType = StringUtility.substringBetween(elementValue.getElementType(), "{", "}");
Map<String, String>  eleTypeMap = new HashMap<String, String>();
StringTokenizer token = new StringTokenizer(elementType, ",");
while(token.hasMoreElements()){
    String str = token.nextToken();
    StringTokenizer furtherToken = new StringTokenizer(str,"=");
    while(furtherToken.hasMoreTokens()){
        eleTypeMap.put(furtherToken.nextToken(), furtherToken.nextToken());
    }
}
Artjom B.
  • 61,146
  • 24
  • 125
  • 222