2015-05-15 5 views
2

Я занимаюсь разработкой приложенияJava, который читает много данных строк любит это:Список строки с вхождений подсчета и сортировки

1 cat (first read) 
2 dog 
3 fish 
4 dog 
5 fish 
6 dog 
7 dog 
8 cat 
9 horse 
...(last read) 

Мне нужен способ, чтобы сохранить все пары [строка, случаи] в порядке от последнего читать, прежде всего, читать.

строка   вхождений
лошадь     1 (первая печать)
кот       собака         рыбы         2 (последняя печать)

На самом деле я использую два списка:
1) List<string> input; где я добавить все данные
В моем примере:

input.add("cat"); 
input.add("dog"); 
input.add("fish"); 
... 

2) List<string> possibilities; где я вставить строки один раз таким образом:

if(possibilities.contains("cat")){ 
    possibilities.remove("cat"); 
} 
possibilities.add("cat"); 

Таким образом, у меня есть отсортированный список, где есть все возможности. Я использую его так:

int occurrence; 
for(String possible:possibilities){ 
    occurrence = Collections.frequency(input, possible); 
    System.out.println(possible + " " + occurrence); 
} 

Этот трюк работает хорошо, но это слишком медленно (у меня есть миллионы входных данных) ... любая помощь?
(английский не является моим родным языком, поэтому, пожалуйста, простите ошибки.)

+0

сделать 'TreeMap ', который будет лучшим решением, как я могу думать. – Prashant

+0

«TreeMap» будет заказывать строковыми клавишами, а не последним. – RealSkeptic

+0

@RealSkeptic, я думаю, ему нужен ручной заказ по-своему. и я понял, что это не может быть TreeMap – Prashant

ответ

1

Используйте Map<String, Integer> как @radoslaw заостренные, чтобы сохранить вставку сортировки использовать LinkedHashMap и не TreeMap, как описано here:

LinkedHashMap хранит ключи в том порядке, в котором они были вставлены, в то время как TreeMap хранится в сортире с помощью компаратора или естественного сопоставимого упорядочения элементов.

Представьте у вас есть все строки в какой-то массив, назовем его listOfAllStrings, перебирать этот массив и использовать строку как key в вашей карте, если он не существует, положить на карте, если она существует, то сумма 1 к фактическому результату ...

Map<String, Integer> results = new LinkedHashMap<String, Integer>(); 
for (String s : listOfAllStrings) { 
    if (results.get(s) != null) { 
     results.put(s, results.get(s) + 1); 
    } else { 
     results.put(s, 1); 
    } 
} 
+0

Но как это соблюдение порядка записей? – RealSkeptic

+0

вы не можете использовать 'new Map ();' как свой интерфейс. отредактируйте свой ответ, сделайте его «TreeMap» – Prashant

+0

исправлено, извините, написал ответ «на лету» со своего мобильного телефона ... xD –

0

использование сделать из TreeMap, который будет держать заказ на клавишах, как указано в compare вашего MyStringComparator обработки класса класса MyString который оборачивает строковых добавление индексов вставки, как это:

// this better be immutable 
class MyString { 
    private MyString() {} 
    public static MyString valueOf(String s, Long l) { ... } 
    private String string; 
    private Long index; 
    public hashcode(){ return string.hashcode(); } 
    public boolean equals() { // return rely on string.equals() } 
} 

class MyStringComparator implements Comparator<MyString> { 
    public int compare(MyString s1, MyString s2) { 
     return -s1.getIndex().compareTo(s2.gtIndex()); 
    } 
} 

Pass компаратор при построении карты:

Map<MyString,Integer> map = new TreeMap<>(new MyStringComparator()); 

Затем, во время разбора ввода, сделать

Long counter = 0; 
while (...) { 
    MyString item = MyString.valueOf(readString, counter++); 
    if (map.contains(item)) { 
     map.put(map.get(item)+1); 
    } else { 
     map.put(item,1); 
    } 
} 

Будет создано множество экземпляров из-за непреложного класса, а компаратор не будет соответствовать равным, но он должен работать.

Отказ от ответственности: это непроверенный код, чтобы показать, что я буду делать, я вернусь и перепроверяю его, когда получаю компромисс.

0

Вот полное решение для вашей проблемы,

import java.util.ArrayList; 
import java.util.Collections; 
import java.util.HashMap; 
import java.util.List; 
import java.util.Map; 

public class DataDto implements Comparable<DataDto>{ 

    public int count = 0; 
    public String string; 
    public long lastSeenTime; 

    public DataDto(String string) { 
     this.string = string; 
     this.lastSeenTime = System.currentTimeMillis(); 
    } 

    public boolean equals(Object object) { 
     if(object != null && object instanceof DataDto) { 
      DataDto temp = (DataDto) object; 
      if(temp.string != null && temp.string.equals(this.string)) { 
       return true; 
      } 
     } 
     return false; 
    } 

    public int hashcode() { 
     return string.hashCode(); 
    } 

    public int compareTo(DataDto o) { 
     if(o != null) { 
      return o.lastSeenTime < this.lastSeenTime ? -1 : 1; 
     } 
     return 0; 
    } 

    public String toString() { 
     return this.string + " : " + this.count; 
    } 

    public static final void main(String[] args) { 
     String[] listOfAllStrings = {"horse", "cat", "dog", "fish", "cat", "fish", "dog", "cat", "horse", "fish"}; 
     Map<String, DataDto> results = new HashMap<String, DataDto>(); 
     for (String s : listOfAllStrings) { 
      DataDto dataDto = results.get(s); 
      if(dataDto != null) { 
       dataDto.count = dataDto.count + 1; 
       dataDto.lastSeenTime = System.nanoTime(); 
      } else { 
       dataDto = new DataDto(s); 
       results.put(s, dataDto); 
      } 
     } 
     List<DataDto> finalResults = new ArrayList<DataDto>(results.values()); 
     System.out.println(finalResults); 
     Collections.sort(finalResults); 
     System.out.println(finalResults); 
    } 
} 

Ans

[horse : 1, cat : 2, fish : 2, dog : 1] 
[fish : 2, horse : 1, cat : 2, dog : 1] 

Я думаю, что это решение будет подходить для вашего требования.

0

Если вы знаете, что ваши данные не будут превышать объем вашей памяти, когда вы все прочитаете в памяти, тогда решение будет простым - с использованием LinkedList или a и LinkedHashMap.

Например, если вы используете Связанный список:

LinkedList<String> input = new LinkedList(); 

Вы затем продолжайте использовать input.add() как вы сделали первоначально. Но когда список ввода заполнен, вы в основном используете решение Jordi Castilla, но помещаете записи в связанный список в обратном порядке. Чтобы сделать это, вы делаете:

Iterator<String> iter = list.descendingIterator(); 
    LinkedHashMap<String,Integer> map = new LinkedHashMap<>(); 

    while (iter.hasNext()) { 
     String s = iter.next(); 
     if (map.containsKey(s)) { 
      map.put(s, map.get(s) + 1); 
     } else { 
      map.put(s, 1); 
     } 
    } 

Теперь, единственное реальное различие между его решением и моим в том, что я использую list.descendingIterator(), который является методом LinkedList, который дает вам запись в обратном порядке, от «лошадей "на" кошку ".

LinkedHashMap будет содержать правильный порядок - все, что было введено первым, будет напечатано первым, и поскольку мы ввели вещи в обратном порядке, то все, что было прочитано последним, будет напечатано первым. Так что, если вы печатаете ваш map результат будет:

{horse=1, cat=2, dog=4, fish=2}

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

private static class Entry implements Comparable<Entry> { 

    private static long nextOrder = Long.MIN_VALUE; 
    private String str; 
    private int frequency = 1; 
    private long order = nextOrder++; 
    public Entry(String str) { 
     this.str = str; 
    } 

    public String getString() { 
     return str; 
    } 

    public int getFrequency() { 
     return frequency; 
    } 

    public void updateEntry() { 
     frequency++; 
     order = nextOrder++; 
    } 

    @Override 
    public int compareTo(Entry e) { 
     if (order > e.order) 
      return -1; 
     if (order < e.order) 
      return 1; 
     return 0; 
    } 

    @Override 
    public String toString() { 
     return String.format("%s: %d", str, frequency); 
    } 
} 

Хитрость здесь в том, что каждый раз, когда вы обновляете запись (добавьте к частоте), его также обновляет заказ.Но метод compareTo() заказывает Entry объекты из высокий заказ (обновлен/вставлен позже) до низкий заказ (обновлен/вставлен ранее).

Теперь вы можете использовать простой HashMap<String,Entry> для хранения информации, как вы читаете это (я предполагаю, что вы читаете с каким-то сканером):

Map<String,Entry> m = new HashMap<>(); 

    while (scanner.hasNextLine()) { 
     String str = scanner.nextLine(); 
     Entry entry = m.get(str); 
     if (entry == null) { 
      entry = new Entry(str); 
      m.put(str, entry); 
     } else { 
      entry.updateEntry(); 
     } 
    } 

    Scanner.close(); 

Теперь вы можете сортировать значение записей :

List<Entry> orderedList = new ArrayList<Entry>(m.values()); 
    m = null; 
    Collections.sort(orderedList); 

Запуск System.out.println(orderedList) даст вам:

[horse: 1, cat: 2, dog: 4, fish: 2]

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

0

Что вы можете сделать:

  1. Обратный порядок списка с помощью Collections.reverse(input). Это выполняется в линейном времени - O (n);
  2. Создайте Set из списка входных данных. A Набор гарантирует уникальность. Чтобы сохранить порядок вставки, вам понадобится LinkedHashSet;
  3. Идите по этому набору, точно так же, как вы сделали выше.

Код:

/* I don't know what logic you use to create the input list, 
* so I'm using your input example. */ 
List<String> input = Arrays.asList("cat", "dog", "fish", "dog", 
      "fish", "dog", "dog", "cat", "horse"); 
/* by the way, this changes the input list! 
* Copy it in case you need to preserve the original input. */ 
Collections.reverse(input); 
Set<String> possibilities = new LinkedHashSet<String>(strings); 

for (String s : possibilities) { 
    System.out.println(s + " " + Collections.frequency(strings, s)); 
} 

Выход:

horse 1 
cat 2 
dog 4 
fish 2 
Смежные вопросы