在通讯录应用中,快速搜索匹配符合关键字的联系人,是一个必备的需求。比如要搜索 名字 尼古拉斯赵四 ,电话号码 1234567 这个联系人,我们可以输入:
1.中文全名 尼古拉斯赵四
2.中文部分名字 赵四
3.拼音全拼 nigulasizhaosi
4.拼音部分 nigu
5.拼音部分首字 ngls
6.拼音首字和部分全拼 ngulasi
7.电话号码匹配
如此多的搜索方式,如果没有一个比较恰当的算法,会导致有很多的if else判断,逻辑也会显得比较复杂。下面是我总结的一个高效的算法。
对于电话号码和纯中文的匹配,应该是没什么难度的,直接检索就可以了,难点是拼音首字母和拼音全拼的组合差异。解决方法就是在联系人的数据结构中准备两个字符串集合,一个集合存储每个中文字的拼音首字母,一个集合存储每个中文字的全拼,在检索的时候对这两个集合做适当的排列组合,去匹配拼音可能的组合方式。
public class ContactItem {
private String name; //名字
private char firstChar; //索引的首字母
private String number; //电话号码
private List<String> fullpingyins = new ArrayList<>();
private List<String> firstpingyins = new ArrayList<>();
Pattern p = Pattern.compile("[\u4E00-\u9FA5a-zA-Z]+");
Matcher m = p.matcher(item.getName());
if(m.lookingAt()){
//如果是以汉字或字母开头,则获取拼音字符串和首字符
int end = m.end();
String substr = item.getName().substring(0,end);
char [] namechars = substr.toCharArray();
//获取全拼
for(char c : namechars){
String pingyin = getPingyin(c);
item.getFullpingyins().add(pingyin);
}
//每个字的首字母
char [] firstpingyins = getFirstSpell(substr).toCharArray();
for (char c : firstpingyins){
item.getFirstpingyins().add(String.valueOf(c));
}
item.setFirstChar(firstpingyins[0]);
}
把数据准备好之后,接下来就是匹配搜索
public void search(final String keystr, final boolean onlyNumber){
Thread thread = new Thread(){
@Override
public void run() {
List<ContactItem> matchlist = new ArrayList<>();
for (ContactItem item : contactItems){
Pattern pattern = Pattern.compile(keystr);
Matcher matcher = null;
//匹配全名
StringBuffer stringBuffer = new StringBuffer();
matcher = pattern.matcher(stringBuffer.append(item.getFullpingyins()));
if(matcher.find()){
matchlist.add(item);
continue;
}
//匹配缩写
stringBuffer = new StringBuffer();
matcher = pattern.matcher(stringBuffer.append(item.getFirstpingyins()));
if(matcher.lookingAt()){
matchlist.add(item);
continue;
}
if(matcher.find()){
matchlist.add(item);
continue;
}
boolean isfind = false;
//缩写加全写,先把缩写逐个替换到全称中匹配
for(int f = 0 ; f < item.getFirstpingyins().size() ; f++){
StringBuffer sb = new StringBuffer();
for (int k =0 ; k < item.getFullpingyins().size() ;k ++){
if(f == k){
sb.append(item.getFirstpingyins().get(f));
continue;
}
sb.append(item.getFullpingyins().get(k));
}
matcher = pattern.matcher(sb);
if(matcher.lookingAt()){
matchlist.add(item);
isfind = true;
break;
}
}
if(isfind){
continue;
}
//缩写加全写,把全拼逐个替换到缩写中匹配
for(int f = 0 ; f < item.getFullpingyins().size() ; f++){
StringBuffer sb = new StringBuffer();
for (int k =0 ; k < item.getFirstpingyins().size() ;k ++){
if(f == k){
sb.append(item.getFullpingyins().get(f));
continue;
}
sb.append(item.getFirstpingyins().get(k));
}
matcher = pattern.matcher(sb);
if(matcher.lookingAt()){
matchlist.add(item);
isfind = true;
break;
}
}
if(isfind){
continue;
}
//匹配号码
if(TextUtils.isEmpty(item.getNumber())){
continue;
}
matcher = pattern.matcher(item.getNumber());
if(matcher.find()){
matchlist.add(item);
continue;
}
}
}
};
thread.start();
}