2015-07-01 1 views
12

У меня есть строка, как это:Разбивает строку в пар ключ-значение

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, если нет значений.

+3

Не могли бы вы объяснить, почему вы думаете, что это не эффективно? Кстати, это не codereview.stackexchange.com, вы должны попробовать его там. – Tom

+0

Прежде всего, я новичок в Java и всегда думал, что есть более простой способ делать то, что я делаю. Далее я использовал 2 объекта массива String и дважды назвал функцию split. Также я использую t [0] и t [1], которые могут вызывать исключение ArrayIndexOutOfBounds, если нет значений. – v1shnu

+0

Этот метод абсолютно прекрасен. –

ответ

11

Вы можете сделать один вызов 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)); 
    } 

выше является вероятно немного более эффективным, чем ваше решение, но если вы нашли код более понятным, а затем сохранить его, потому что почти нулевой шанс, такая оптимизация оказывает значительное влияние на производительность, если вы не делаете этого миллионы раз. В любом случае, если это так важно, вы должны измерить и сравнить.

EDIT:

для тех, кто задается вопросом, что означает ::? в приведенном выше коде: String.split() принимает регулярное выражение в качестве аргумента. Разделитель - это подстрока, которая соответствует регулярному выражению. ::? - это регулярное выражение, которое означает: 1 двоеточие, за которым следует 0 или 1 двоеточие. Таким образом, это позволяет рассматривать :: и : в качестве разделителей.

+0

ничего себе! это заботится обо всем, что я считал неэффективным. Но еще одно. Я изменил строку на это «местоположение: Japan :: food: sushi ::: cool», так что значение cool не имеет ключа. Но все равно вывод: это круто местоположение Япония еда is sushi – v1shnu

+0

только что обнаружил, что ключ для значения «cool» - это просто пустая строка. – v1shnu

+2

Возможно, было бы полезно кратко объяснить, что означает '::?' (И упоминать очевидную альтернативу ': | ::' для тех, у кого есть одна и та же проблема, но разные разделители). – Dukeling

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

Надежда это поможет :)

+0

Я думаю, что new_test не понадобится, поскольку вы можете сделать это в самом тесте test1, как String [] test1 = test.replaceAll ("::", ":"). Split (":"); Спасибо в любом случае :) – v1shnu

+0

@ViChU ya that is true :) – Vishnu

+0

вызов replaceAll не очень эффективен, чем вызов split – Martijn

-1

Вашей программе абсолютно нормально.

Просто потому, что вы попросили более оптимальный код.

Я уменьшил вашу память, приняв несколько переменных вместо того, чтобы брать массивы и хранить в них.

Посмотрите на свою строку, это следует за patter.

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

Это не займет много итераций на расщеплении каждый раз.

Не занимает много памяти.

Временная сложность О (п)

Выход:

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

Ваш код забудет последнюю пару ключ/значение. В результате нет foo/sushi. Я действительно сомневаюсь, что это более эффективно: этот код создает целые временные строковые объекты, которые нужно собрать в мусор. –

+0

лучше, чем тратить память массивов, которая уже находится в строке. –

+0

@UmaKanth этот фрагмент вызывает charAt несколько раз. Все хорошо ? – v1shnu

11

Используя библиотеку 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, так как он не создает регулярное выражение для "::".

Update даже обрабатывает правильно угловой корпус из комментариев:

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} 
+0

Nice! Спасибо за это. Я не знаю, является ли это наиболее эффективным, но это то, что я должен иметь в виду при работе со строками, подобными этим. – v1shnu

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 раза быстрее. Для более длинных строк разница, вероятно, несколько больше.

Но ваша версия все еще только 415 миллисекунд для 100 000 повторений, где эта составляет 99 миллисекунд.

+0

Ну, чтобы ответить на ваш вопрос. Есть еще много организаций, которые не адаптировались к последним стандартам.В моем случае эти данные поступают в систему POS. В Америке огромное количество розничных магазинов, и в каждом розничном магазине есть много счетчиков POS, и каждая транзакция в счетчике отправляет эти данные. И по данным, она не включает только эту строку. Эта строка похожа на одну строку в файле размером 10000 строк xml. Таким образом, существует требование, чтобы код был максимально эффективным. И, следовательно, этот вопрос возник в моем сознании :) – v1shnu

+0

Ну, мои тесты показывают, что на очень хлипкой инфраструктуре (идеон) 10 000 линий занимают 44 миллисекунды. Насколько быстро ваше требование? Что делает остальная часть кода? Сколько времени он тратит на синтаксический анализ строк? – Martijn

+0

Весь процесс походит на действительно ОГРОМНУЮ. Я могу сказать, что эта строка входит в класс класса 3000, который выполняет много операций чтения, дешифрования, отображения карт, доступа к базе данных и JMS. Подумайте, что вы покупаете iPhone, эта строка рассказывает мне о типе используемой вами карты. – v1shnu

Смежные вопросы