将字符串拆分为键值对

Split string into key-value pairs

我有这样的字符串:

pet:cat::car:honda::location:Japan::food:sushi

现在 : 表示键值对,而 :: 分隔键值对。 我想将键值对添加到映射中。

我可以使用:

Map<String, String> map = new HashMap<String, String>();
String test = "pet:cat::car:honda::location:Japan::food:sushi";
String[] test1 = test.split("::");

for (String s : test1) {
    String[] t = s.split(":");
    map.put(t[0], t[1]);
}

for (String s : map.keySet()) {
    System.out.println(s + " is " + map.get(s));
}

但是有没有一种有效的方法来做到这一点?


感觉代码效率很低,因为我用了2个String[]对象,调用了两次split函数。 此外,我正在使用 t[0]t[1] 如果没有值,它们可能会抛出 ArrayIndexOutOfBoundsException

您可以使用以下代码调用一次 split() 并传递一次字符串。但它当然首先假定字符串是有效的:

    Map<String, String> map = new HashMap<String, String>();
    String test = "pet:cat::car:honda::location:Japan::food:sushi";

    // split on ':' and on '::'
    String[] parts = test.split("::?");

    for (int i = 0; i < parts.length; i += 2) {
        map.put(parts[i], parts[i + 1]);
    }

    for (String s : map.keySet()) {
        System.out.println(s + " is " + map.get(s));
    }

以上可能比你的解决方案效率高一点,但如果你发现你的代码更清晰,那就保留它,因为这样的优化几乎没有机会为零对性能有重大影响,除非你这样做数百万次。总之,既然这么重要,那你就去衡量和比较吧。

编辑:

对于那些想知道 ::? 在上面的代码中意味着什么的人来说: String.split() 将正则表达式作为参数。分隔符是与正则表达式匹配的子字符串。 ::? 是一个正则表达式,意思是:1 个冒号,后跟 0 或 1 个冒号。因此,它允许将 ::: 视为分隔符。

我不知道这是不是最好的方法,但我认为这是另一种不使用拆分方法来做同样事情的方法

Map<String, String> map = new HashMap<String, String>();
String test = "pet:cat::car:honda::location:Japan::food:sushi";
String[] test1 = test.replaceAll("::",":").split(":");
for(int i=0;i<test1.length;i=i+2)
{
     map.put(test1[i], test1[i+1]);
}

for (String s : map.keySet()) {
    System.out.println(s + " is " + map.get(s));
}

希望对您有所帮助:)

你的程序绝对没问题。

只是因为您要求更优化的代码。

我通过使用少量变量而不是使用数组并存储在其中来减少你的内存。

看看你的字符串,它遵循一种模式。

key : value :: key : value ::....

我们能从中做些什么?

获取密钥直到它是 : ,一旦它达到 : 就得到值直到它达到 '::'。

package qwerty7;

import java.util.HashMap;

public class Demo {
public static void main(String ar[])
{
    StringBuilder s = new StringBuilder("pet:cat::car:honda::location:Japan::food:sushi");
    boolean isKey = true;
    String key = "", value = "";
    HashMap<String, String> hm = new HashMap();
    for(int i = 0; i < s.length(); i++)
    {
        char ch = s.charAt(i);
        char nextChar = s.charAt(i+1);
        if(ch == ':' && nextChar != ':')
        {
            isKey = false;
            continue;
        }
        else if(ch == ':' && nextChar == ':')
        {
            hm.put(key, value);
            isKey = true;
            key = "";
            value = "";
            i+=1;
            continue;
        }
        if(isKey)
        {
            key += ch;
        }
        else
        {
            value += ch;
        }
         if(i == s.length() - 1)
            {
                hm.put(key, value);
            }

    }
    for (String x : hm.keySet()) {
        System.out.println(x + " is " + hm.get(x));
    }
}
}

Doing so doesn't take up much iterations on splitting each time.

Doesn't take up much memory.

Time complexity O(n)

输出:

car is honda
location is Japan
pet is cat
food is sushi

使用 Guava 库,它是一个单行代码:

String test = "pet:cat::car:honda::location:Japan::food:sushi";
Map<String, String> map = Splitter.on( "::" ).withKeyValueSeparator( ':' ).split( test );
System.out.println(map);

输出:

{pet=cat, car=honda, location=Japan, food=sushi}

这也可能比 JDK String.split 更快,因为它不会为 "::".

创建正则表达式

更新 它甚至可以正确处理评论中的极端情况:

String test = "pet:cat::car:honda::location:Japan::food:sushi:::cool";
Map<String, String> map = Splitter.on( "::" ).withKeyValueSeparator( ':' ).split( test );
System.out.println(map);

输出为:

{pet=cat, car=honda, location=Japan, food=sushi, =cool}

你的方案确实有些低效。

给你解析字符串的人也是个小丑。有行业标准的序列化格式,如 JSON 或 XML,存在快速、高效的解析。发明方轮从来都不是什么好主意。

第一个问题:你关心吗?它是否足够慢以致影响应用程序的性能?可能不会,但只有一种方法可以找出答案。对您的代码进行基准测试。

也就是说,存在更有效的解决方案。下面是一个例子

public static void main (String[] args) throws java.lang.Exception
{
    String test = "pet:cat::car:honda::location:Japan::food:sushi";
    boolean stateiskey = true;

    Map<String, String> map = new HashMap<>();
    int keystart = 0;
    int keyend = 0;
    int valuestart = 0;
    int valueend = 0;

    for(int i = 0; i < test.length(); i++){
        char nextchar = test.charAt(i);
        if (stateiskey) {
            if (nextchar == ':') {
              keyend = i;           
              stateiskey = false;
              valuestart = i + 1;
            }
        } else {
            if (i == test.length() - 1 || (nextchar == ':' && test.charAt(i + 1) == ':')) {
                valueend = i;
                if (i + 1 == test.length()) valueend += 1; //compensate one for the end of the string
                String key = test.substring(keystart, keyend);
                String value = test.substring(valuestart, valueend);
                keystart = i + 2;
                map.put(key, value);
                i++;
                stateiskey = true;
            }
        }
    }

    System.out.println(map);
}

这个解决方案是一个只有两个状态的有限状态机。它只查看每个字符两次,一次是在测试边界时,一次是在将其复制到地图中的新字符串时。这是最低金额。

它不会创建不需要的对象,例如 stringbuilder、字符串或数组,这样可以保持较低的收集压力。

它保持良好的地方性。下一个字符可能总是在缓存中,因此查找很便宜。

它付出了巨大的代价,但可能不值得:

  • 它要复杂得多,也不那么明显
  • 有各种各样的活动部件
  • 当您的字符串采用非预期格式时,调试起来会更加困难
  • 你的同事会讨厌你
  • 非要debug什么的你会恨死你的

值得吗?可能是。您需要以多快的速度准确解析该字符串?

https://ideone.com/8T7twy 的一个快速而肮脏的基准测试告诉我,对于这个字符串,这个方法大约快 4 倍。对于更长的字符串,差异可能会更大。

但是你的版本仍然只有 415 毫秒重复 100.000 次,而这个是 99 毫秒。

尝试此代码 - 请参阅注释以获取解释:

HashMap<String,String> hmap = new HashMap<>();
String str="abc:1::xyz:2::jkl:3";
String straraay[]= str.split("::?");

for(int i=0;i<straraay.length;i+=2) {
    hmap.put(straraay[i],straraay[i+1]);
}

for(String s:straraay){
    System.out.println(hmap.values()); //for Values only
    System.out.println(hmap.keySet()); //for keys only if you want to more clear
}

这可能会有用。 *utm_source=test_source&utm_medium=test_medium&utm_term=test_term& utm_content=test_content&utm_campaign=test_name&referral_code=DASDASDAS

   String str[] = referrerString.split("&");
                    HashMap<String,String> stringStringHashMap= new HashMap<>();
                    List<String> al;
                    al = Arrays.asList(str);
                    String[] strkey ;

                for (String s : al) {
                    strkey= s.split("=");
                    stringStringHashMap.put(strkey[0],strkey[1]);


                }
                for (String s : stringStringHashMap.keySet()) {
                    System.out.println(s + " is " + stringStringHashMap.get(s));
                }