предисловие
В последнее время было немного загружено, и каталог этой статьи был выбит в самолете.
Ближе к дому, обновлено на прошлой неделеcimПервый выпуск, я не ожидал, что откликнутся на восторженные отклики, и это было самое времяGitHub Trending Java
Первое место на форуме, получил 300+ звезд за один день.
Сейчас в общей сложности 1,3 тысячи звезд, а в тесте приняли участие десятки друзей. Большое спасибо за вашу поддержку.
В процессе я также получил некоторыеbug
Обратная связь,feature
Предложения; поэтому за это время я поставил некоторые ошибки с большей отдачей и те, которые требуют более срочной помощи.feature
Скорректировано, это обновлениеv1.0.1
Версия:
- Клиент автоматически отключается по истечении времени ожидания.
- новый
AI
модель. - Запрос истории чата.
- Нечеткое соответствие префикса пользователя в сети.
Давайте поговорим о некоторых наиболее важных функциях.
客户端超时自动下线
Эта функция включает в себя сердцебиение дизайна клиента и сервера, который более интересен и наступил на несколько ям, поэтому я планирую оставить его для следующего раза, чтобы поговорить отдельно.
режим ИИ
Все еще должны помнить, что раньше это взорвало круг друзей.估值两个一个亿的 AI 核心代码
.
Это как нельзя лучше подходит для моей сцены.
Поэтому я добавил команду для включения режима AI одним щелчком мыши.Использование выглядит следующим образом.
Добро пожаловать, чтобы обновить исходный код, пожалуйста, напишите мне в личные сообщения для финансирования 🤣.
запись чата
История чатов также является более актуальной функцией.
использовать команду:q 关键字
Вы можете запросить записи чата, связанные с отдельными людьми.
Эта функция на самом деле относительно проста, ее нужно сохранять только при отправке и получении сообщения.
Но следует учитывать, что это сохранение сообщения является операцией ввода-вывода, которая неизбежно занимает много времени; необходимо максимально избегать влияния на отправку и получение сообщений.
Писать сообщения асинхронно
Поэтому процесс написания сообщения завершается асинхронно, что не влияет на реальный бизнес.
Кроме того, ее очень просто реализовать, что является типичной моделью производитель-потребитель.
После того, как основной поток получает сообщение, он сразу записывает в очередь, а другой поток продолжает брать данные из очереди и сохраняет запись чата.
Примерный код выглядит следующим образом:
При написании сообщения будет открыт поток, потребляющий сообщение:
Окончательная стратегия хранения записей сообщений после рассмотрения по-прежнему сохраняется на клиенте самым простым способом, что может уменьшить сложность.
Проще говоря, записывается на диск по текущей дате + имя пользователя.
Когда клиент закрывается, поток очереди потребления останавливается посредством прерывания потока.
На самом деле конструкция этогоlogback
Способ написания логов аналогичен, если интересно, можете пройтись по нимlogback
исходный код для более подробной информации.
интерфейс обратного вызова
Что касается приема сообщений от других клиентов, для записи в журнал используется ранее зарезервированный интерфейс обратного вызова сообщений.
После получения сообщения будет выполнен настраиваемый интерфейс обратного вызова.
Следовательно, логика записи может быть реализована в этом методе обратного вызова, а когда в будущем появятся другие логики обработки сообщений, ее также можно будет добавить напрямую сюда.
Когда логика обработки увеличивается, лучше перейти на режим цепочки ответственности, который понятнее и проще в обслуживании.
алгоритм поиска
Далее следует алгоритм поиска, который является важным предметом обсуждения в этой статье, а точнее, алгоритм префиксного нечеткого сопоставления.
Достигаемый эффект следующий:
использовать команду:qu prefix
Информацию о пользователе можно искать по префиксу.
Конечно, в командной строке это не имеет особого смысла, но на мобильном терминале это действительно полезно. Аналогично сопоставлению WeChat по имени пользователя:
Поскольку я планирую разработать мобильное приложение позже, я сначала реализовал эту функцию.
Это видно и по эффекту: соответствие строки по входному префиксу (сейчас поддерживается только английский язык).
Самая быстрая и простая реализация без каких-либо ограничений может напрямую хранить все строки в контейнере (List, Set) и проходить их одну за другой при запросе; используйтеString.startsWith("prefix")
соответствовать.
Но с этим есть несколько проблем:
- Ресурсы хранения расточительны, будь то список или набор, будут дополнительные потери.
- Эффективность запроса низкая, и необходимо пройти по коллекции, а затем по строке.
char
множество (String.startsWith
реализация).
словарное дерево
Исходя из вышеперечисленных вопросов, мы можем рассмотреть следующее:
Предположим, мне нужно хранитьjava,javascript,jsp,php
Как эти строки будут храниться в ArrayList?
Очевидно, что он будет полностью храниться в массиве, в то же время этот массив также может быть потрачен впустую и не израсходован.
Но на самом деле, если вы внимательно посмотрите на эти данные, то обнаружите некоторые общие черты, такие какjava,javascript
иметь общий префиксjava
;а такжеjsp
иметь общий префиксj
.
Можно ли использовать эти префиксы? Это экономит на одну копию меньше.
например, писатьjava,javascript
Структура этих двух строк следующая:
при внесенииjsp
Время:
депозит наконецjsf
Время:
Я считаю, что все должны были понять, что по этому способу хранения можно сэкономить много памяти, а эффективность запросов также относительно высока.
Например, запрос сjav
Данные в начале, нужно только начать с началаj
Начните запрашивать вниз, и, наконец, вы найдетеava
так же какscript
Эти два узла, поэтому символы, встречающиеся на всем пути запроса, пишутся вместе, чтобы быть результатом запроса.java+javascript
.
Если сb
В начале запроса будет возвращен сразу первый шаг, что лучше, чем вlist
Эффективность гораздо выше.
Но этот график не идеален, потому что я не знаю, когда запрос соответствует ранее записанной строке.
Например, откуда вы знаете на картинке выше?
j+ava
тот, который мы написали ранееjava
Что можно сказать об этом персонаже.
Поэтому нам нужнополная строкаДанные отмечены маркером:
Например, мы будемava、script、p、f
Эти узлы представлены цветом. Указывает, что результат соответствует запросу этого символа.
и нашелs
Цвет этого символа неправильный, значит нужно продолжить проверку.
например, ввод ключевых словjs
При сопоставлении, когда его путь запроса переходит кs
В это время считается, что цвет s неправильный, поэтому он не будетjs
как результат матча. Вместо этого продолжайте проверку и обнаружите, что есть два дочерних узла p и f с правильными цветами, поэтому путь запросаjsp
а такжеjsf
как совпадающий результат.
И просто введите j, все цветные символы ниже будут написаны вместе как результирующий набор.
На самом деле это типичное дерево словаря.
Реализация
Ниже приводится конкретная реализация кода.На самом деле, алгоритм не так прост в использовании анализа текста, как реализация бизнес-функции, конкретный код можно понять, посмотрев исходный код и отладив больше.
Поговорим о нескольких ключевых моментах:
Реализация узла дерева словаря, гдеisEnd
Эквивалент цвета на картинке.
использоватьNode[] children
для хранения дочерних узлов.
Чтобы запрос был чувствителен к регистру, длина дочернего узла эквивалентна26*2
.
ввод данных
В качестве примера для одного теста записываются три строки, и конечная структура данных выглядит следующим образом:
Есть несколько отличий между картинкой и картинкой выше:
- Каждый узел является символом, так что высота дерева достигает 52.
- Дочерние узлы каждого узла представляют собой массивы длиной 52, поэтому нижний индекс массива может использоваться для представления символьного значения, которое он представляет. Например, 0 — это большая А, 26 — маленькая А и так далее.
- чем-то похож на упомянутый ранееФильтр Блума, что экономит память.
debug
Также видно, что структура данных соответствует рисунку выше:
Итак, реальные этапы написания следующие:
- Разделите строку на массив символов и определите случай, чтобы вычислить ее позицию в массиве.
index
. - Добавить новый узел по индексу массива дочерних узлов текущего узла.
- Если это последний символ, установите только что добавленный узел как последний узел, то есть измените цвет узла выше.
- Наконец, укажите текущий узел на следующий узел, чтобы продолжить запись.
Запрос вообще более хлопотный, по сути это глубокий обход дерева, конечную мысль можно понять глядя на картинку.
Итак, эта структура используется для нечеткого сопоставления в cim.
Исходный код словарного дерева находится здесь:
На самом деле, эту структуру также можно использовать для определения того, находится ли слово с определенным префиксом в определенной куче данных, сколько раз встречается слово с определенным префиксом и т. д.
Суммировать
В настоящее времяcimОн все еще находится в горячей бета-версии (хотя в группе всего несколько человек 20), заинтересованные друзья могут приватно пообщаться со мной и пригласить вас присоединиться к нам☺️
ничего новогоBUG
Мы сосредоточимся на завершении этих функций до того, как они будут выпущены, и никаких сюрпризов не будет обновлено на следующей неделе.cim
Восстановление связи с сердцебиением и другие механизмы.
Полный исходный код:
Если эта статья полезна для вас, пожалуйста, не стесняйтесь пересылать ее.