ArrayList 的字符串不是此任务的正确数据结构。搜索此类列表的唯一方法是遍历所有列表,这会很慢。您需要一个数据结构来支持您正在执行的查询类型。这样做的代价可能是增加了内存占用。
字符的树形结构似乎可以很好地工作。您可以将单词 AAB、ABA 和 ABB 表示为以下树:
A
/ \
A B
/ / \
B A B
我也非常同意 GhostCat 的观点,即您可能不想在客户端执行此操作。
下面是一个快速实现。我不保证它完美或最佳地工作 - 它是对可能的演示,而不是生产就绪代码。我没有用大数据集测试过。
它只支持你的下划线规则,但它应该足够简单以适应它以支持你的多字符匹配。
由根节点和实际 char 节点实现的通用接口,并包含默认搜索实现:
interface CharTree
{
List<CharNode> getChildren();
default Optional<CharNode> getChild(char character)
{
return getChildren().stream()
.filter(ch -> ch.getCharacter() == character)
.findFirst();
}
default void search(final String pattern, final StringBuilder builder, final Set<String> results)
{
if (pattern.isEmpty())
{
results.add(builder.toString());
return;
}
char character = pattern.toCharArray()[0];
final List<CharNode> candidates;
if (character == '_')
{
candidates = getChildren();
}
else
{
candidates = getChild(character)
.map(Collections::singletonList)
.orElse(Collections.emptyList());
}
for (final CharNode node : candidates)
{
builder.append(node.getCharacter());
node.search(pattern.substring(1, pattern.length()), builder, results);
builder.deleteCharAt(builder.length() - 1);
}
}
}
基本根实现,使用静态方法构建树:
class Root implements CharTree
{
private Root() { }
@Getter private List<CharNode> children = new ArrayList<>();
public static Root buildTree(final List<String> words)
{
final Root root = new Root();
for (final String word : words)
{
CharTree current = root;
for (char character : word.toCharArray())
{
Optional<CharNode> node = current.getChild(character);
if (node.isPresent())
{
current = node.get();
}
else
{
final CharNode tmp = new CharNode(character);
current.getChildren().add(tmp);
current = tmp;
}
}
}
return root;
}
}
简单的字符节点(注解来自龙目岛)
@Data
@ToString(of = "character")
class CharNode implements CharTree
{
private final char character;
private List<CharNode> children = new ArrayList<>();
}
一些单元测试以防万一:
@Test
public void one()
{
final List<String> words = Arrays.asList("aaa", "bbb", "ccc");
final CharTree root = Root.buildTree(words);
final Set<String> results = new HashSet<>();
root.search("aaa", new StringBuilder(), results);
Assert.assertEquals(1, results.size());
Assert.assertTrue(results.contains("aaa"));
}
@Test
public void two()
{
final List<String> words = Arrays.asList("aaa", "aba", "abb");
final CharTree root = Root.buildTree(words);
final Set<String> results = new HashSet<>();
root.search("a_a", new StringBuilder(), results);
Assert.assertEquals(2, results.size());
Assert.assertTrue(results.contains("aaa"));
Assert.assertTrue(results.contains("aba"));
}
@Test
public void three()
{
final List<String> words = Arrays.asList("aaa", "aba", "abb");
final CharTree root = Root.buildTree(words);
final Set<String> results = new HashSet<>();
root.search("___", new StringBuilder(), results);
Assert.assertEquals(3, results.size());
Assert.assertTrue(results.contains("aaa"));
Assert.assertTrue(results.contains("aba"));
Assert.assertTrue(results.contains("abb"));
}