将字符串拆分为键值对

时间:2015-07-01 06:11:56

标签: java hashmap

我有一个这样的字符串:

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

7 个答案:

答案 0 :(得分:15)

使用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}

答案 1 :(得分:13)

您可以使用以下代码单独调用split()和String上的单个传递。但它当然假设String首先是有效的:

    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个冒号。因此,它允许将:::视为分隔符。

答案 2 :(得分:2)

你的解决方案确实效率低下。

给你解析字符串的人也有点像小丑。存在行业标准序列化格式,如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);
}

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

它不会创建不需要的对象,如字符串构建器,字符串或数组,这会使收集压力降低。

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

它的成本很高,但可能不值得:

  • 它更复杂,更不明显
  • 有各种各样的活动部件
  • 当字符串处于意外格式时,调试起来比较困难
  • 你的同事会讨厌你
  • 当你需要调试某些内容时,你会讨厌你
值得吗?也许。你需要多快解析一下这个字符串?

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

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

答案 3 :(得分:1)

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

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
}

答案 4 :(得分:0)

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

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));
}

希望它会有所帮助:)

答案 5 :(得分:0)

这可能有用。 * 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));
                }

答案 6 :(得分:-1)

你的节目绝对没问题。

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

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

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

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));
    }
}
}
  

这样做不会在每次分割时占用很多迭代次数。

     

不占用太多记忆。

     

时间复杂度O(n)

<强>输出:

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