Java 将字符串拆分为键值对
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/31153753/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Split string into key-value pairs
提问by v1shnu
I have a string like this:
我有一个这样的字符串:
pet:cat::car:honda::location:Japan::food:sushi
Now :
indicates key-value pairs while ::
separates the pairs.
I want to add the key-value pairs to a map.
现在:
指示键值对,同时::
分隔对。我想将键值对添加到地图中。
I can achieve this using:
我可以使用以下方法实现这一点:
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));
}
But is there an efficient way of doing this?
但是有没有一种有效的方法来做到这一点?
I feel the code is inefficient because I have used 2 String[]
objects and called the split
function twice.
Also, I am using t[0]
and t[1]
which might throw an ArrayIndexOutOfBoundsException
if there are no values.
我觉得代码效率低下,因为我使用了 2 个String[]
对象并split
两次调用了该函数。另外,我正在使用t[0]
and如果没有值,t[1]
它可能会抛出一个ArrayIndexOutOfBoundsException
。
采纳答案by JB Nizet
You could do a single call to split() and a single pass on the String using the following code. But it of course assumes the String is valid in the first place:
您可以使用以下代码对 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));
}
The above is probablya little bit more efficient than your solution, but if you find your code clearer, then keep it, because there is almost zero chance such an optimization has a significant impact on performance, unless you do that millions of times. Anyway, if it's so important, then you should measure and compare.
以上可能比您的解决方案更有效一点,但是如果您发现您的代码更清晰,那么请保留它,因为这种优化对性能产生重大影响的可能性几乎为零,除非您这样做了数百万次。无论如何,如果它如此重要,那么您应该衡量和比较。
EDIT:
编辑:
for those who wonder what ::?
means in the above code: String.split() takes a regular expression as argument. A separator is a substring that matches the regular expression. ::?
is a regular expression which means: 1 colon, followed by 0 or 1 colon. It thus allows considering ::
and :
as separators.
对于那些想知道::?
上面代码中的含义的人: String.split() 将正则表达式作为参数。分隔符是与正则表达式匹配的子字符串。::?
是一个正则表达式,意思是:1 个冒号,后跟 0 或 1 个冒号。因此,它允许考虑::
和:
作为分隔符。
回答by Vishnu
I don't know this is best approach or not but i think this is another way of doing same thing without using split method twice
我不知道这是否是最好的方法,但我认为这是在不使用 split 方法两次的情况下做同样事情的另一种方式
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));
}
Hope it will help :)
希望它会有所帮助:)
回答by Uma Kanth
Your program is absolutely fine.
你的程序绝对没问题。
Just because you asked for a more optimal code.
仅仅因为您要求更优化的代码。
I reduced your memory by taking few variables instead of taking arrays and storing in them.
我通过使用少量变量而不是使用数组并将其存储在其中来减少您的记忆。
Look at your string it follows a patter.
看看你的字符串,它遵循一个模式。
key : value :: key : value ::....
key : value :: key : value ::....
What can we do from this?
我们可以从中做什么?
get the key till it is :
, once it reaches :
get value until it reaches '::'.
获取密钥直到它是:
,一旦它达到:
获取值,直到它达到'::'。
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)
这样做不会在每次拆分时占用太多迭代。
不占用太多内存。
时间复杂度 O(n)
Output:
输出:
car is honda
location is Japan
pet is cat
food is sushi
回答by Tagir Valeev
Using Guava library it's a one-liner:
使用 Guava 库,它是单行的:
String test = "pet:cat::car:honda::location:Japan::food:sushi";
Map<String, String> map = Splitter.on( "::" ).withKeyValueSeparator( ':' ).split( test );
System.out.println(map);
The output:
输出:
{pet=cat, car=honda, location=Japan, food=sushi}
This also might work faster than JDK String.split
as it does not create a regexp for "::"
.
这也可能比 JDK 更快,String.split
因为它不会为"::"
.
Updateit even handles correctly the corner case from the comments:
更新它甚至可以正确处理评论中的极端情况:
String test = "pet:cat::car:honda::location:Japan::food:sushi:::cool";
Map<String, String> map = Splitter.on( "::" ).withKeyValueSeparator( ':' ).split( test );
System.out.println(map);
The output is:
输出是:
{pet=cat, car=honda, location=Japan, food=sushi, =cool}
回答by Martijn
Your solution is indeed somewhat inefficient.
您的解决方案确实有些低效。
The person who gave you the string to parse is also somewhat of a clown. There are industry standard serialization formats, like JSON or XML, for which fast, efficient parses exist. Inventing the square wheel is never a good idea.
给你解析字符串的人也有点像小丑。有一些行业标准的序列化格式,如 JSON 或 XML,它们存在快速、高效的解析。发明方轮从来都不是一个好主意。
First question: Do you care? Is it slow enough that it hinders performance of your application? It's likely not to, but there is only one way to find out. Benchmark your code.
第一个问题:你在乎吗?它是否足够慢以致妨碍您的应用程序的性能?可能不会,但只有一种方法可以找出答案。对您的代码进行基准测试。
That said, more efficient solutions exist. Below is an example
也就是说,存在更有效的解决方案。下面是一个例子
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);
}
This solution is a finite state machine with only two states. It looks at every character only twice, once when it tests it for a boundary, and once when it copies it to the new string in your map. This is the minimum amount.
这个解决方案是一个只有两个状态的有限状态机。它只查看每个字符两次,一次是在测试边界时,一次是将其复制到地图中的新字符串中。这是最低金额。
It doesn't create objects that are not needed, like stringbuilders, strings or arrays, this keeps collection pressure low.
它不会创建不需要的对象,如字符串构建器、字符串或数组,这使收集压力保持在较低水平。
It maintains good locality. The next character probably always is in cache, so the lookup is cheap.
它保持良好的位置。下一个字符可能总是在缓存中,因此查找成本很低。
It comes at a grave cost that is probably not worth it though:
它付出了沉重的代价,但可能不值得:
- It's far more complicated and less obvious
- There are all sorts of moving parts
- It's harder to debug when your string is in an unexpected format
- Your coworkers will hate you
- You will hate you when you have to debug something
- 它要复杂得多,也不那么明显
- 有各种各样的活动部件
- 当您的字符串采用意外格式时更难调试
- 你的同事会讨厌你
- 当你必须调试某些东西时,你会讨厌你
Worth it? Maybe. How fast do you need that string parsed exactly?
值得?也许。您需要多快准确解析该字符串?
A quick and dirty benchmark at https://ideone.com/8T7twytells me that for this string, this method is approximately 4 times faster. For longer strings the difference is likely somewhat greater.
https://ideone.com/8T7twy 上的一个快速而肮脏的基准测试告诉我,对于这个字符串,这种方法大约快 4 倍。对于更长的字符串,差异可能更大一些。
But your version is still only 415 milliseconds for 100.000 repetitions, where this one is 99 milliseconds.
但是您的版本对于 100.000 次重复仍然只有 415 毫秒,而这个是 99 毫秒。
回答by Debabrata Barik
Try this code - see the comments for an explanation:
试试这个代码 - 请参阅注释以获取解释:
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
}
回答by Visal Varghese
This might be useful. *utm_source=test_source&utm_medium=test_medium&utm_term=test_term& utm_content=test_content&utm_campaign=test_name&referral_code=DASDASDAS
这可能有用。 *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));
}