16

I have TreeMap<String,String> which I need to convert to URI-like string and then back to Map. I need to set custom delimiters.

Is there any tool (Guava, Apache commons?) that can do it for me? I know, I can write simple loops, but I'm looking for one-liner :)

For example

key    value
key1   val1
key2   val2

key1_val1|key2_val2
karolkpl
  • 2,189
  • 10
  • 39
  • 60

4 Answers4

43

According to David Tulig you could do it in guava via

 String string = Joiner.on("|").withKeyValueSeparator("_").join(map);

The opposite is also available via

 Map<String, String> map = Splitter.on("|").withKeyValueSeparator("_").split(string);
AlikElzin-kilaka
  • 34,335
  • 35
  • 194
  • 277
zapl
  • 63,179
  • 10
  • 123
  • 154
  • 2
    Great answer. Just want to add Joiner does not handle null value out of the box, useForNull("") fixes the problem: Map map = Splitter.on("|").useForNull("").withKeyValueSeparator("_").split(string); – Christopher Yang Oct 09 '14 at 21:15
  • Great answer, but how can i modify the result after i run splitter? i got error Collections$UnmodifiableMap.put – MNFS Jul 02 '19 at 13:51
2

Using Java8:

private static Map<String, String> prepareMap() {
    Map<String, String> map = new LinkedHashMap<>();
    map.put("key1", "val1");
    map.put("key2", "val2");
    return map;
}

@Test
public void toStr() {
    assertEquals("key1_val1|key2_val2", prepareMap().entrySet().stream()
            .map(e -> e.getKey() + "_" + e.getValue())
            .collect(Collectors.joining("|")));
}

@Test
public void toStrFunction() {
    assertEquals("key1_val1|key2_val2", joiner("|", "_").apply(prepareMap()));
}

private static Function<Map<String, String>, String> joiner(String entrySeparator, String valueSeparator) {
    return m -> m.entrySet().stream()
            .map(e -> e.getKey() + valueSeparator + e.getValue())
            .collect(Collectors.joining(entrySeparator));
}

@Test
public void toMap() {
    assertEquals("{key1=val1, key2=val2}", Stream.of("key1_val1|key2_val2".split("\\|"))
            .map(e -> e.split("_", 2))
            .collect(Collectors.toMap(e -> e[0], e -> e.length > 1 ? e[1] : null)).toString());
}

@Test
public void toMapFunction() {
    assertEquals("{key1=val1, key2=val2}", splitter("\\|", "_").apply("key1_val1|key2_val2").toString());
}

private static Function<String, Map<String, String>> splitter(String entrySeparator, String valueSeparator) {
    return s -> Stream.of(s.split(entrySeparator))
            .map(e -> e.split(valueSeparator, 2))
            .collect(Collectors.toMap(e -> e[0], e -> e.length > 1 ? e[1] : null));
}
jokster
  • 577
  • 5
  • 14
1

its not guava or apache commons, and it is a loop, but aside from instantiating the string builder, it is a one liner:

for (Entry<String,String> entry : myMap.entrySet()) {
    sb.append(entry.getKey() + separator + entry.getValue() + "\n");
}
WarrenFaith
  • 57,492
  • 25
  • 134
  • 150
yurib
  • 8,043
  • 3
  • 30
  • 55
  • Which isn't exactly as OP wants, especially since you have to get the string from stringbuilder and also omit last entry separator, which will drag this over "oneliner". True, in java you can put everything on one line, but there has to be some limit to that :) – Enerccio Jan 16 '14 at 16:05
  • 4
    I like that a StringBuilder is used but the passed parameter is with + concatenated. Oh the irony is a fine one... oh and "\n" is not a constant! Masterpiece! – WarrenFaith Jan 16 '14 at 16:09
  • Well I guess the idea was it to be a oneliner – Enerccio Jan 16 '14 at 16:13
  • it is still a one line loop content. Beside that his oneliner was still flawed missing 2 variables... and a bad coding style does not make a oneliner a valid solution :) Everybody can write a oneliner class... no magic there – WarrenFaith Jan 16 '14 at 16:15
  • 1
    @WarrenFaith: For what crazy reason you want "\n" to be a constant? – maaartinus Jan 16 '14 at 16:26
  • 1
    @maaartinus for the same reason "separator" might be a constant: memory. Each time you call append() with "something" an anonymous variable will be created with the content of "something". Call that in a loop(n) and you have n times "something" stored in your memory. Using a constant you would only pass the memory address and no real content. – WarrenFaith Jan 16 '14 at 16:29
  • @WarrenFaith: `"\n"` is a `String` literal. `String` literals are all interned... there is only one instance of the literal `"\n"` in memory. – ColinD Jan 16 '14 at 16:38
  • 1
    @WarrenFaith: This isn't true. "\n" is a compile-time constant and gets interned. If you mean a local anonymous variable, that's JIT's job, not mine. No heap memory gets allocated because of the use of "\n" and that's all. – maaartinus Jan 16 '14 at 16:38
  • Ok, learned something new. Another argument is code style. Why is separator a constant/variable and "\n" not? Wouldn't it be better to have this defined as a constant to have one place across the code to change "\n" to, lets say "\n\r"? Keyword: Maintainability – WarrenFaith Jan 16 '14 at 16:42
  • @WarrenFaith: That depends on the application. There's no inherent reason that separator needs to be a variable rather than a literal either... unless there is, such as if the separator is passed in from somewhere else for whatever reason. And if you want the line separator to be platform dependent, sure you could make it a variable. Or get it from `System.getProperty("line.separator")`. But again, it all depends on the application, and for an example like this it doesn't really matter either way. – ColinD Jan 16 '14 at 16:47
-2

In java 8 and up there is another way without external dependencies: use StringJoiner:

    List<String> cities = Arrays.asList("Milan", 
                                        "London", 
                                        "New York", 
                                        "San Francisco");

    String citiesCommaSeparated = String.join(",", cities);

    System.out.println(citiesCommaSeparated);


    //Output: Milan,London,New York,San Francisco

Credits and example go to this URL (https://reversecoding.net/java-8-convert-list-string-comma/)

Timo Böwing
  • 313
  • 1
  • 2
  • 8