简体   繁体   English

当字符串在 Java 8 中没有分隔符时,根据提供的键从字符串创建 Map

[英]Creating a Map from a string based on the provided Keys when the string doesn't have delimiters in Java 8

Given the following String :给定以下String

String s = "DIMENSION 24cm 34cm 12cm DETAILED SPECIFICATION Twin/Twin XL Flat Sheet: 105"l x 74"w. CARE For best results, machine wash warm with like colors. COLOURS red blue green"; 

Keys are = DIMENSIONS, DETAILED SPECIFICATION, CARE, COLOURS关键是 = DIMENSIONS, DETAILED SPECIFICATION, CARE, COLOURS

We need to create Map<String,String> where keys will be as provided above and corresponding text will be the value .我们需要创建Map<String,String> ,其中将如上所示,相应的文本将是

The map's contents will look like:地图的内容将如下所示:

DIMENSION: 24cm 34cm 12cm,
DETAILED SPECIFICATION: Twin/Twin XL Flat Sheet: 105"l x 74"w,
CARE:  For best results, machine wash warm with like colors,
COLOURS: red blue green 

And not necessary that all these keys and values are present in the string.并且没有必要所有这些都存在于字符串中。

Suppose the key CARE is not present in the input String :假设输入String中不存在CARE

String s = "DIMENSION 24cm 34cm 12cm DETAILED SPECIFICATION Twin/Twin XL Flat Sheet: 105"l x 74"w. COLOURS red blue green"; 

The map's contents will look like:地图的内容将如下所示:

DIMENSION: 24cm 34cm 12cm,
DETAILED SPECIFICATION: Twin/Twin XL Flat Sheet: 105"l x 74"w,
COLOURS: red blue green 

Ie if a key is absent in the given string then the corresponding value will be also absent.即,如果给定字符串中不存在,则相应的也将不存在。 For instance, DIMENSION key is absent and string starts like "DETAILED SPECIFICATION ... " .例如, DIMENSION不存在,字符串以"DETAILED SPECIFICATION ... "开头。

As the string doesn't have delimiters, I am unable to create a map from it.由于字符串没有分隔符,我无法从中创建地图。

With plane Java, I am able to do like this:使用平面 Java,我可以这样做:

if(s.contains("ASSEMBLY")) {
    ass = s.substring(s.indexOf("COLOURS") + 8);
    s = s.replaceAll(s.substring(s.indexOf("COLOURS")),"");
}
if(s.contains("OVERALL")){
    ov = s.substring(s.indexOf("CARE") + 5);
    s = s.replaceAll(s.substring(s.indexOf("CARE")),"");
}
if(s.contains("CARE")){
    care1 = s.substring(s.indexOf("DETAILED SPECIFICATION") + 24);
    s = s.replaceAll(s.substring(s.indexOf("DETAILED SPECIFICATION")),"");
}
if(s.contains("DIMENSIONS")){
    de1 = s.substring(s.indexOf("DIMENSIONS") + 11);
    s =s.replaceAll(s.substring(s.indexOf("DIMENSIONS")),"");
}

If we have delimiter, then I am able to do it like this.如果我们有分隔符,那么我可以这样做。

Map<String, String> map = Stream.of(s)
    .map(s -> s.split("="))
    .collect(Collectors.toMap(s -> s[0], s -> s[1]));

Regex approach:正则表达式方法:

    String s = "DIMENSION 24cm 34cm 12cm DETAILED SPECIFICATION Twin/Twin XL Flat Sheet: 105 w. COLOURS red blue green";
    String[] keys = new String[]{"DIMENSION", "DETAILED SPECIFICATION", "CARE", "COLOURS"};
    String keyRegex = String.join("|", keys);
    Pattern p = Pattern.compile("(?<key>" + keyRegex + ") (?<value>((?!(" + keyRegex + ")).)*)");
    Matcher m = p.matcher(s);

    Map<String, String> result = new HashMap<>();
    while (m.find()) {
        result.put(m.group("key"), m.group("value"));
    }

You can generate a regular expression from the given keys and compile it into a pattern .您可以从给定的生成正则表达式并将其编译为模式

I approached this problem using lookahead and lookbehind :我使用lookaheadlookbehind解决了这个问题:

public static Pattern getPattern(Collection<String> keys) {
    String joinedKeys = String.join("|", keys);
    String regex = String.format("(?<=%s)|(?=%s)", joinedKeys, joinedKeys); // creates a regex "(?<=DIMENSION|DETAILED\\sSPECIFICATION|CARE|COLOURS)|(?=DIMENSION|DETAILED\\sSPECIFICATION|CARE|COLOURS)"
    return Pattern.compile(regex);
}
  • (?=foo) - Lookahead - matches a position that immediately follows after the foo ; (?=foo) - Lookahead - 匹配紧跟在foo之后的位置;
  • (?<=foo) - Lookbehind - matches a position that immediately precedes the foo . (?<=foo) - Lookbehind - 匹配紧接在foo之前的位置。

For more information have a look at this tutorial有关更多信息,请查看本教程

When we have a pattern , we can generate a stream using Pattern.splitAsStream() .当我们有一个模式时,我们可以使用Pattern.splitAsStream()生成一个流。

public static Map<String, String> toMap(String source, Set<String> keys) {
    
    return getPattern(keys).splitAsStream(source)
        .collect(combineByKey(keys));
}

Each element of this a stream would be either a key or a value , and in order to obtain a map a result of the stream execution, we need a collector which will be capable to distinguish between a key from a value .这个流的每个元素要么是要么是,为了获得流执行结果的映射,我们需要一个能够区分收集器

We can create such a collector using Collector.of() .我们可以使用Collector.of()创建这样的收集器。 As its a mutable container , I'll use a Deque of map-entries .作为一个可变容器,我将使用map-entriesDeque

A case with an empty string used as a key that can be observed in the accumulator function below represents a situation which might take place while executing the stream in parallel when a thread gets the piece of data which starts from a value and not from a key , in other words, a key and a value and up in different containers .可以在下面的累加器函数中观察到的将空字符串用作的情况表示当线程获取从而不是开始的数据时并行执行流时可能发生的情况,换句话说,一个和一个在不同的容器中 This problem gets fixed when we're merging containers in the combiner function.当我们在组合器函数中合并容器时,这个问题得到了解决。

StringBuilder is used as an intermediate type of value an entry returned by Map.entry() is immutable. StringBuilder用作的中间类型,由Map.entry()返回的条目是不可变的。

Note: Map.entry() was introduced with Java 9. To make this solution compliant with Java 8 use new AbstractMap.SimpleEntry<>() instead ( see the code for JDK 8 ).注意: Map.entry()是在 Java 9 中引入的。要使此解决方案与Java 8兼容,请改用new AbstractMap.SimpleEntry<>()请参阅 JDK 8 的代码)。

public static Collector<String, ?, Map<String, String>> combineByKey(Set<String> keys) {

    return Collector.of(
        ArrayDeque::new,
        (Deque<Map.Entry<String, StringBuilder>> deque, String next) -> {
            if (keys.contains(next)) deque.add(Map.entry(next, new StringBuilder()));
            else {
                if (deque.isEmpty()) deque.add(Map.entry("", new StringBuilder(next)));
                else deque.getLast().getValue().append(next);
            }
        },
        (left, right) -> {
            if (!right.isEmpty() && !left.isEmpty() && right.getFirst().getKey().isEmpty()) {
                left.getLast().getValue().append(right.pollFirst().getValue());
            }
            left.addAll(right);
            return left;
        },
        deque -> deque.stream().collect(Collectors.toMap(
            Map.Entry::getKey,
            entry -> entry.getValue().toString().strip()
        ))
    );
}

main()

public static void main(String[] args) {
    String source = "DIMENSION 24cm 34cm 12cm DETAILED SPECIFICATION Twin/Twin XL Flat Sheet: 105l x 74w. CARE For best results, machine wash warm with like colors. COLOURS red blue green";

    Set<String> keys = Set.of("DIMENSION", "DETAILED SPECIFICATION", "CARE", "COLOURS");

    Map<String, String> result = toMap(source, keys); // converting the source string to map
    
    result.forEach((k, v) -> System.out.println(k + " -> " + v)); // printing the result
}

Output:输出:

COLOURS -> red blue green
DIMENSION -> 24cm 34cm 12cm
DETAILED SPECIFICATION -> Twin/Twin XL Flat Sheet: 105l x 74w.
CARE -> For best results, machine wash warm with like colors.

A link to Online Demo在线演示的链接

This might help you:这可能会帮助您:

String str = "DIMENSION 24cm 34cm 12cm DETAILED SPECIFICATION Twin/Twin XL Flat Sheet: 105 w. CARE For best results, machine wash warm with like colors. COLOURS red blue green";

    String[] keys = {"DIMENSIONS", "DETAILED SPECIFICATION", "CARE", "COLOURS"};

    Map<String, String> map = new LinkedHashMap<>();

    for (int index = 0; index < keys.length; index++) {

        String subString = "";
        if(index + 1 < keys.length) {
            int start = str.indexOf(keys[index] + " ") > 0 ? str.indexOf(keys[index] + " ") + keys[index].length() : str.indexOf(keys[index]);
            int end = str.indexOf(keys[index + 1]);
            if(start < 0 || end < 0) {
                continue;
            }
            subString = str.substring(start, end);
        }  else {
            subString = str.substring(str.indexOf(keys[index] + " "));
        }
        map.put(keys[index], subString);
    }
    System.out.println(map);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM