Хорошо, вы понимаете, почему это занимает много времени, верно?
У вас есть строки размером 1 МБ, и для каждого токена замена выполняет итерацию по 1 МБ и создание новой копии объемом 1 МБ. Ну, не точная копия, поскольку любой найденный токен заменяется новым значением токена. Но для каждого токена вы читаете 1 МБ, обновляете 1 МБ памяти и пишете 1 МБ.
Теперь, мы можем придумать лучший способ сделать это? Как насчет того, чтобы вместо итерации 1 МБ строки для каждого токена, мы вместо этого пройдемся по ней один раз.
Прежде чем идти по нему, мы создадим пустую строку вывода.
Когда мы проходим по исходной строке, если мы найдем токен, мы прыгнем вперед token.length()
символов и выпишем запутанный токен. В противном случае мы перейдем к следующему символу.
По сути, мы выворачиваем процесс наизнанку, делаем цикл for для длинной строки и в каждой точке ищем токен. Чтобы сделать это быстрым, нам понадобится быстрая петля для токенов, поэтому мы помещаем их в некий ассоциативный массив (набор).
Я понимаю, почему так долго,
но не уверен в исправлении. За каждый 1 МБ
Строка, на которой я исполняю
замены у меня от 1 до 2 тысяч
токаны я хочу заменить. Так гуляет
персонаж за персонажем ищет любой
тысячи токенов не кажется
быстрее
В общем, что занимает больше всего времени в программировании? Новая память.
Теперь, когда мы создаем StringBuffer, вероятно, что происходит выделение некоторого объема пространства (скажем, 64 байта), и что всякий раз, когда мы добавляем больше его текущей емкости, он, вероятно, удваивает его пространство. И затем копирует старый символьный буфер к новому. (Возможно, мы можем перераспределить С, и не нужно копировать.)
Итак, если мы начнем с 64 байтов, чтобы получить до 1 МБ, мы выделяем и копируем:
64, затем 128, затем 256, затем 512, затем 1024, затем 2048 ... мы делаем это двадцать раз, чтобы получить до 1 МБ. И чтобы попасть сюда, мы выделили 1 МБ только для того, чтобы выбросить его.
Предварительное выделение с использованием функции, аналогичной функции reserve()
в C ++, по крайней мере, позволит нам сделать это сразу. Но это все равно для каждого токена. Вы, по крайней мере, создаете временную строку размером 1 МБ для каждого токена. Если у вас есть 2000 токенов, вы выделяете около 2 миллиардов байтов памяти, и все это в итоге составляет 1 МБ. Каждый 1 Мбайт выброс содержит преобразование предыдущей результирующей строки с применением текущего токена.
И вот почему это так долго.
Теперь да, решение о том, какой токен применять (если есть) для каждого символа, также требует времени. Возможно, вы захотите использовать регулярное выражение, которое внутренне создает конечный автомат для выполнения всех возможностей, а не поиск по множеству, как я предлагал изначально. Но то, что действительно убивает вас, - это время выделить всю эту память для 2000 копий строки размером 1 МБ.
Дэн Гибсон предлагает:
Сортируйте свои токены, чтобы вам не пришлось
искать по тысяче жетонов каждый
персонаж. Сортировка займет некоторое
время, но это, вероятно, в конечном итоге
быть быстрее, так как вам не нужно
искать тысячи токенов каждый
характер.
Это было моё рассуждение о том, чтобы поместить их в ассоциативный массив (например, Java HashSet). Но другая проблема заключается в сопоставлении, например, если один токен является «а», а другой - «ан» - если есть какие-либо общие префиксы, то есть как мы сопоставляем?
Здесь ответ Келтекса пригодится: он делегирует сопоставление Regex, что является отличной идеей, поскольку Regex уже определяет (жадное совпадение) и реализует, как это сделать. После того, как сопоставление установлено, мы можем изучить захваченное, а затем использовать карту Java (также ассоциативный массив), чтобы найти запутанный токен для сопоставленного, необсуждаемого.
Я хотел сосредоточить свой ответ не только на том, как это исправить, но и на том, почему возникла проблема.