在內存中對 List 集合進行模糊查詢是常見需求。本文介紹兩種主流方案:基於正則表達式的傳統循環過濾,以及 Java 8+ Stream API 的函數式寫法,並對比性能與適用場景。
class Employee {
private String name; private int age;
// getter/setter/toString 省略
}
List list = Arrays.asList(
new Employee("wangqiang", 30),
new Employee("lisi", 25),
new Employee("zhangsan", 28)
);
// 需求:查詢名字包含 "wang" 的員工
public List search(String name, List list) {
List result = new ArrayList<>();
Pattern pattern = Pattern.compile(name); // 編譯正則
for (Employee emp : list) {
Matcher matcher = pattern.matcher(emp.getName());
if (matcher.matches()) { // 整串匹配
result.add(emp);
}
}
return result;
}
Pattern pattern = Pattern.compile(name, Pattern.CASE_INSENSITIVE);
if (matcher.find()) { // 子串匹配
result.add(emp);
}
關鍵區別:matches() 要求整個字符串符合正則;find() 只要字符串中包含匹配子串即可。模糊查詢必須用 find()。
若用戶輸入 wa*g 期望匹配 wang,需將 * 轉為正則 .*:
String regex = name.replace("*", ".*").replace("?", ".");
Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
public List searchStream(String keyword, List list) {
return list.stream()
.filter(emp -> emp.getName().toLowerCase().contains(keyword.toLowerCase()))
.collect(Collectors.toList());
}
public List searchRegex(String regex, List list) {
Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
return list.stream()
.filter(emp -> pattern.matcher(emp.getName()).find())
.collect(Collectors.toList());
}
return list.parallelStream() // 并行流
.filter(emp -> emp.getName().contains(keyword))
.collect(Collectors.toList());
// Guava
List result = list.stream()
.filter(Predicates.containsPattern(Pattern.compile(keyword, Pattern.CASE_INSENSITIVE)))
.collect(Collectors.toList());
// Apache Commons Collections 4
Collection result = CollectionUtils.select(list,
emp -> emp.getName().toLowerCase().contains(keyword.toLowerCase()));
| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| 正則+循環 | 靈活支持複雜正則;兼容舊版本 | 代碼冗長;手動編譯 Pattern 易忘記複用 | 需複雜正則規則;Java 7 環境 |
| Stream API | 代碼簡潔;易並行;鏈式調用 | 調試稍難;Lambda 序列化限制 | Java 8+ 通用首選 |
| contains/ indexOf | 無正則開銷;速度最快 | 不支持通配符/正則 | 簡單子串匹配,極高性能要求 |
| 第三方庫 | 豐富謂詞組合 | 引入依賴 | 已使用對應庫的項目 |
String.contains:簡單子串查詢無需正則,性能提升 10 倍以上。Map> 索引或使用內存數據庫(H2、SQLite)。parallelStream 適合數萬條以上數據,小集合開銷反而更大。emp.getName() 可能為 null,建議 Objects.toString(emp.getName(), "")。選擇合適的工具,寫出既優雅又高效的查詢代碼。