Java 存在重复键时 guava Splitter 失败

Java guava Splitter failing when there are duplicate keys

我们如何在使用 java guava Splitter 函数时处理重复键。这是遇到以下问题的示例代码。有没有更好的方法来处理这个问题。

String fieldSplit = " ";
String valueSplit = "=";
String message = "ip=1.2.9.0 error=NA ip=1.2.9.0";
Map<String, String> parserMap = Splitter.on(fieldSplit).omitEmptyStrings().withKeyValueSeparator(valueSplit).split(message);

Exception in thread "kv-plugin-ac801a38-66f1-4ffe-86ca-f9eb6c823842-StreamThread-1" org.apache.kafka.streams.errors.StreamsException: Exception caught in process. taskId=0_0, processor=KSTREAM-SOURCE-0000000000, topic=kv-input, partition=0, offset=22, stacktrace=java.lang.IllegalArgumentException: Duplicate key [ip] found.

我收到上述错误。有人可以建议一种更好的方法来处理这个问题。因为我是 java.

的新手

取决于您要对重复键执行的操作。 Map<String, String>是键值存储,只能有唯一键,也只能有一个值。

如果你想存储所有这些值,你需要像 Map<String, List<String> 或 Guava Multimap.

这样的东西

在这种情况下,您无法使用 Splitter 执行此操作,因为它无法处理重复键。你需要自己写逻辑。

String fieldSplit = " ";
String valueSplit = "=";
String message = "ip=1.2.9.0 error=NA ip=1.2.9.0";

Map<String, List<String>> parserMap = new HashMap<>();

for (String part : message.split(" ")) {
  String[] subparts = part.split("=", 2);
  if (!parserMap.contains(subparts[0])) {
     parserMap.put(subparts[0], new ArrayList<>());
  }
  parserMap.get(subparts[0]).add(subparts[1]);
}

如果您想省略那些重复的条目,您仍然可以将 Map<String, String> 与类似的东西一起使用。


Map<String, String> parserMap = new HashMap<>();

for (String part : message.split(" ")) {
  String[] subparts = part.split("=", 2);
  if (!parserMap.contains(subparts[0])) {
     parserMap.put(subparts[0], subparts[1]);
  }
}

使用重复的键是一种 documented behavior of MapSplitter#split, so depending on what you want, you have to write your own "key-value" spliter consisting of two splitters. Please look at examples below, you can collect results to map with desired behavior (overwrite or discard) or even try out collecting to ListMultimap,但它会使结果的值存储在列表中,即使只有一个值也是如此。

public class SO66139006 {

    private static final Splitter PAIRS_SPLITTER = Splitter.on(' '); // .trimResults().omitEmptyStrings() if necessary
    private static final Splitter KEY_VALUE_SPLITTER = Splitter.on('=').limit(2);

    @Test
    public void shouldOverwriteValuesOnDuplicateKey() {
        //given
        String message = "ip=42.42.42.0 error=NA ip=1.2.9.0";
        //when
        Map<String, String> result = parseOverwritingValues(PAIRS_SPLITTER, KEY_VALUE_SPLITTER, message);
        //then
        assertThat(result) // {ip=1.2.9.0, error=NA}
                .containsExactly(entry("ip", "1.2.9.0"), entry("error", "NA"));
    }

    private Map<String, String> parseOverwritingValues(Splitter pairsSplitter, Splitter keyValueSplitter, String message) {
        return Streams.stream(pairsSplitter.split(message))
                .map(keyValueSplitter::splitToList)
                .collect(toImmutableMap(
                        list -> list.get(0),
                        list -> list.get(1),
                        (oldValue, newValue) -> newValue
                ));
    }

    @Test
    public void shouldDiscardValuesOnDuplicateKey() {
        //given
        String message = "ip=42.42.42.0 error=NA ip=1.2.9.0";
        //when
        Map<String, String> result = parseDiscardingValues(PAIRS_SPLITTER, KEY_VALUE_SPLITTER, message);
        //then
        assertThat(result) // {ip=42.42.42.0, error=NA}
                .containsExactly(entry("ip", "42.42.42.0"), entry("error", "NA"));
    }

    private Map<String, String> parseDiscardingValues(Splitter pairsSplitter, Splitter keyValueSplitter, String message) {
        return Streams.stream(pairsSplitter.split(message))
                .map(keyValueSplitter::splitToList)
                .collect(toImmutableMap(
                        list -> list.get(0),
                        list -> list.get(1),
                        (oldValue, newValue) -> oldValue
                ));
    }

    @Test
    public void shouldAppendValuesOnDuplicateKey() {
        //given
        String message = "ip=42.42.42.0 error=NA ip=1.2.9.0";
        //when
        ListMultimap<String, String> result = parseMultipleValues(PAIRS_SPLITTER, KEY_VALUE_SPLITTER, message);
        //then
        assertThat(result.asMap()) // {ip=[42.42.42.0, 1.2.9.0], error=[NA]}
                .containsExactly(entry("ip", ImmutableList.of("42.42.42.0", "1.2.9.0")), entry("error", ImmutableList.of("NA")));
    }

    private ListMultimap<String, String> parseMultipleValues(Splitter pairsSplitter, Splitter keyValueSplitter, String message) {
        return Streams.stream(pairsSplitter.split(message))
                .map(keyValueSplitter::splitToList)
                .collect(toImmutableListMultimap(
                        list -> list.get(0),
                        list -> list.get(1)
                ));
    }

    @Test
    public void shouldThrowByDefault() {
        //given
        String fieldSplit = " ";
        String valueSplit = "=";
        String message = "ip=1.2.9.0 error=NA ip=1.2.9.0";
        //when
        final Throwable throwable = catchThrowable(() -> Splitter.on(fieldSplit).omitEmptyStrings().withKeyValueSeparator(valueSplit).split(message));
        //then
        assertThat(throwable)
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessage("Duplicate key [ip] found.");
    }
}