日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

实现城市列表的排序及模糊查询

發布時間:2023/12/29 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 实现城市列表的排序及模糊查询 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

作者 |?wustor

地址 |?https://www.jianshu.com/p/0ea45116f475

聲明 |?本文是 wustor?原創,已獲授權發布,未經原作者允許請勿轉載



概述

項目需求中有一個需求,是用戶輸入的地址進行智能匹配,包含拼音匹配跟文字匹配,下面先展示一下需要實現的效果

其實看到這個需求,最開始的想法其實是很偷懶的,就是讓服務端寫一個接口,然后進行接口調用,不過在沒網的時候就尷尬了,輸入是沒有提示的,所以這種方式其實不大好,再加上城市地址庫一旦確定基本上就是不會輕易改變的,基于這幾點考慮,打算做一個本地搜索。


正文

確定實現方式之后,其實思路就比較清晰了,首先請求一次接口的數據,然后直接放在本地,再加上項目的需求,所以基本的功能點如下:
主要有以下2點:

  • 對接口返回的數據進行排序

  • 根據排序進行分組

  • 對用戶的輸入進行智能匹配


排序的實現

提到排序,其實首先會點到Java中的兩個接口Comparable跟Comparator

Comparable
public interface Comparable<T> {
? ?public int compareTo(T o);
}


Comparable實際上就只是個接口,定義了一個compareTo方法,挺簡單的,不過在使用的時候需要注意一下幾點:


兩個元素排序:需要實現compareTo方法,并且有一個int返回值,表明返回的結果,具體比較的規則可以根據需求自己定義,可以實現相同類型的參數進行比較.


多個元素排序:這里用地比較多的情況就是排序,JDK提供了一個工具類Arrays,調用Arrays.sort(Object[] a);只需要傳入的數組實現了Comparable接口即可對傳入的數組進行排序,這個時候我們注意到,Arrays.sort有很多重載方法,我們可以看一下



有很多我們熟悉的基本類型,int,byte,char,這些貌似跟Comparable沒有什么關系,不過由于Java是面向對象的,所以對于基本類型有一個裝箱拆箱操作,當看到基本類型的時候,應該多跟他們的包裝類聯系起來,那就隨便找幾個,int的包裝類Integer進行byte的包裝類Byte


public final class Integer extends Number implements Comparable<Integer>{
? ? ?public int compareTo(Integer anotherInteger) {
? ? ? ?return compare(this.value, anotherInteger.value);
? ?}
}
public final class Byte extends Number implements Comparable<Byte>{
? ? ?public int compareTo(Byte anotherByte) {
? ? ? ?return compare(this.value, anotherByte.value);
? ?}
}
public finalclass Character implements java.io.Serializable, Comparable<Character>{
? ? public int compareTo(Character anotherCharacter) {
? ? ? ?return compare(this.value, anotherCharacter.value);
? ?}
}


原來,他們的包裝類都實現了Comparable接口,所以理清了,可以直接調用Arrays的sort方法對這些基本類型進行排序,當然,這里的排序都是基于包裝類自身實現的排序算法,是固定不變的,如果是我們自定義的對象的話,需要重寫compare方法。


Comparator
public interface Comparator<T> {
? ?int compare(T o1, T o2);
? ?boolean equals(Object obj);
}


Comparator的方法比Comparable要多地多,這里選擇了compare跟equals兩個方法,compare很好理解,用來比較兩個對象,equals是用來比較兩個comparator的,如果傳入的對象也是一個Comparator并且他們的排序規則也是一樣的,則equals方法返回true,否則返回false.


  • 兩個元素:直接傳入對象,即可比較

  • 多個元素:Collections提供了sort方法,傳入一個list,跟一個comparator

  • public static <T> void sort(List<T> list, Comparator<? super T> c) {
    ? ? ? ?if (list.getClass() == ArrayList.class) {
    ? ? ? ? ? ?Arrays.sort(((ArrayList) list).elementData, 0, list.size(), (Comparator) c);
    ? ? ? ? ? ?return;
    ? ? ? ?}
    ? ? ? ?Object[] a = list.toArray();
    ? ? ? ?Arrays.sort(a, (Comparator)c);
    ? ? ? ?ListIterator<T> i = list.listIterator();
    ? ? ? ?for (int j=0; j<a.length; j++) {
    ? ? ? ? ? ?i.next();
    ? ? ? ? ? ?i.set((T)a[j]);
    ? ? ? ?}
    ? ?}


    然后方法里面還是調用了Arrays.sort,畢竟集合也是數組,最終還是調用了數組的排序方法。


    對比分析

    Comparator是在類的外部進行排序,Comparable是在類的內部進行排序

    Comparator比較適合對于多個類進行排序,只需要實現一個Comparator就可以,Comparable則需要在每個類中實現Comparable接口


    開始排序

    排序通常的做法是對字母進行排序,但是接口返回的是文字,所以需要將文字轉換成拼音,并且拿到首字母,才能進行排序,這里用到了一個第三方庫TinyPinyin,適用于Java和Android的快速漢字轉拼音庫。


    以武漢為例


  • 用tinypinyin將所有的城市名稱轉換成拼音,用3個字段分別保存W,WH,WUHAN,其中W用來進行排序分組,WH是用來進行簡拼匹配,WUHAN是用來進行全拼匹配

  • 將城市列表的數據根據首字母安裝ABCD的順序進行排序,對于無法獲取拼音的通過"#"進行標識

  • 然后再進行二次分組,ABCD各位一大組,插入一個titleA,titleB,titleC,通過不同的type來在Recyclerview中進行type區分


  • 這些其實沒什么難度,下面貼一下Comparator的代碼,自定義了compare方法,


    @Override
    ? ? ? ?public int compare(CityBean c1, CityBean c2)
    {
    ? ? ? ? ? ?if (c1.getPinyinFirst().equals("#")) {
    ? ? ? ? ? ? ? ?return 1;
    ? ? ? ? ? ?} else if (c2.getPinyinFirst().equals("#")) {
    ? ? ? ? ? ? ? ?return -1;
    ? ? ? ? ? ?}
    ? ? ? ? ? ?return c1.getPinyinFirst().compareTo(c2.getPinyinFirst());
    ? ? ? ?}
    ? ?}

    查找算法

    先定義一下查找規則

  • 如果是漢字,則采用精準查找

  • 如果是字母,當字母數量較小(3個以內)的時候,優先進行簡拼,然后全拼,字母較多,使用全拼查找


  • 正則匹配查找算法


    public static void find(String inputStr, List<CityBean> old, List<CityBean> target) {
    ? ? ? ?if (RegexUtils.isEnglishAlphabet(inputStr)) {
    ? ? ? ? ? ?//拼音模糊匹配
    ? ? ? ? ? ?findByEN(inputStr, old, target);
    ? ? ? ?} else {
    ? ? ? ? ? ?//含有中文精準匹配
    ? ? ? ? ? ?findByCN(inputStr, old, target);
    ? ? ? ?}
    ? ?}


    中文匹配


    private static void findByCN(String inputStr, List<CityBean> mBodyDatas, List<CityBean> searchResult) {
    ? ? ? ?for (int i = 0; i < mBodyDatas.size(); i++) {
    ? ? ? ? ? ?CityBean cityBean = mBodyDatas.get(i);
    ? ? ? ? ? ?if (!TextUtils.isEmpty(cityBean.getRegionName()) && cityBean.getRegionName().contains(inputStr)) {
    ? ? ? ? ? ? ? ?searchResult.add(cityBean);
    ? ? ? ? ? ?}
    ? ? ? ?}
    ? ?}


    字母匹配


    private static void findByEN(String inputStr, List<CityBean> mBodyDatas, List<CityBean> searchResult) {
    ? ? ? ?//把輸入的內容變為大寫
    ? ? ? ?String searPinyin = PinYinUtil.transformPinYin(inputStr);
    ? ? ? ?//搜索字符串的長度
    ? ? ? ?int searLength = searPinyin.length();
    ? ? ? ?//搜索的第一個大寫字母
    ? ? ? ?for (int i = 0; i < mBodyDatas.size(); i++) {
    ? ? ? ? ? ?CityBean cityBean = mBodyDatas.get(i);
    ? ? ? ? ? ?//如果輸入的每一個字母都和名字的首字母一樣,那就可以匹配比如:武漢,WH
    ? ? ? ? ? ?if (cityBean.getMatchPin().contains(searPinyin)) {
    ? ? ? ? ? ? ? ?searchResult.add(cityBean);
    ? ? ? ? ? ?} else {
    ? ? ? ? ? ? ? ?boolean isMatch = false;
    ? ? ? ? ? ? ? ?//先去匹配單個字,比如武漢WU,HAN.輸入WU,肯定匹配第一個
    ? ? ? ? ? ? ? ?for (int j = 0; j < cityBean.getNamePinyinList().size(); j++) {
    ? ? ? ? ? ? ? ? ? ?String namePinyinPer = cityBean.getNamePinyinList().get(j);
    ? ? ? ? ? ? ? ? ? ?if (!TextUtils.isEmpty(namePinyinPer) && namePinyinPer.startsWith(searPinyin)) {
    ? ? ? ? ? ? ? ? ? ? ? ?//符合的話就是當前字匹配成功
    ? ? ? ? ? ? ? ? ? ? ? ?searchResult.add(cityBean);
    ? ? ? ? ? ? ? ? ? ? ? ?isMatch = true;
    ? ? ? ? ? ? ? ? ? ? ? ?break;
    ? ? ? ? ? ? ? ? ? ?}
    ? ? ? ? ? ? ? ?}
    ? ? ? ? ? ? ? ?if (isMatch) {
    ? ? ? ? ? ? ? ? ? ?continue;
    ? ? ? ? ? ? ? ?}
    // ? ? ? ? ? ? ? ?根據拼音包含來實現,比如武漢:WUHAN,輸入WUHA或者WUHAN。
    ? ? ? ? ? ? ? ?if (!TextUtils.isEmpty(cityBean.getNamePinYin()) && cityBean.getNamePinYin().contains(searPinyin)) {
    ? ? ? ? ? ? ? ? ? ?//這樣的話就要從每個字的拼音開始匹配起
    ? ? ? ? ? ? ? ? ? ?for (int j = 0; j < cityBean.getNamePinyinList().size(); j++) {
    ? ? ? ? ? ? ? ? ? ? ? ?StringBuilder sbMatch = new StringBuilder();
    ? ? ? ? ? ? ? ? ? ? ? ?for (int k = j; k < cityBean.getNamePinyinList().size(); k++) {
    ? ? ? ? ? ? ? ? ? ? ? ? ? ?sbMatch.append(cityBean.getNamePinyinList().get(k));
    ? ? ? ? ? ? ? ? ? ? ? ?}
    ? ? ? ? ? ? ? ? ? ? ? ?if (sbMatch.toString().startsWith(searPinyin)) {
    ? ? ? ? ? ? ? ? ? ? ? ? ? ?//匹配成功
    ? ? ? ? ? ? ? ? ? ? ? ? ? ?int length = 0;
    ? ? ? ? ? ? ? ? ? ? ? ? ? ?//比如輸入是WUH,或者WUHA,或者WUHAN,這些都可以匹配上
    ? ? ? ? ? ? ? ? ? ? ? ? ? ?for (int k = j; k < cityBean.getNamePinyinList().size(); k++) {
    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?length = length + cityBean.getNamePinyinList().get(k).length();
    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?if (length >= searLength) {
    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?break;
    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?}
    ? ? ? ? ? ? ? ? ? ? ? ? ? ?}
    ? ? ? ? ? ? ? ? ? ? ? ? ? ?//有可能重復匹配
    ? ? ? ? ? ? ? ? ? ? ? ? ? ?if (!searchResult.contains(cityBean))
    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?searchResult.add(cityBean);
    ? ? ? ? ? ? ? ? ? ? ? ?}
    ? ? ? ? ? ? ? ? ? ?}
    ? ? ? ? ? ? ? ?}
    ? ? ? ? ? ?}
    ? ? ? ?}
    ? ?}


    由于我是在內存中進行匹配查找的,這樣雖然效率比較高,但是進行匹配的時候,過多地使用了for循環,整體的性能不是很好,后續會嘗試著通過Sqlite進行查找,這樣的話,效率可能會高一下,感興趣的可以優化一下。


    源碼下載

    https://github.com/wustor/Localsearchdemo


    與之相關

    2017 | 我在 5 個月時間里分享了 98 篇文章



    總結

    以上是生活随笔為你收集整理的实现城市列表的排序及模糊查询的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。