【问题标题】:Searching a List of Strings (100 000 Strings): How to search for x character if there is a %搜索字符串列表(100 000 个字符串):如果有 %,如何搜索 x 字符
【发布时间】:2018-08-01 10:35:07
【问题描述】:

我正在用 Java 编写一个 android 应用程序,我需要显示 ListView

但在创建ListView 之前,我需要用数据填充我的Adapter。 我有一个字符串的ArrayList,我想用EditText 搜索那个ArrayList

我的Arraylist 有 100 000 多个条目。

  • 我想在输入 ABC 时从 ArrayList 获取 ABC。
  • 我想在输入 A_C 时从 ArrayList 获取 ABC、AAC、ACC、ADC,...,所以在这种情况下,_ 需要替换一个字符。它也应该适用于彼此相邻而不是彼此相邻的多个下划线。
  • 我想在输入 A%C 时从 ArrayList 获取 ABC、AAC、ACC、ADC、ABAC、ABBC 等,因此在这种情况下,% 需要替换多个字符。它也应该适用于不相邻的多个 %。这是我认为与 windows 中的星 * 相同的东西。

希望你能帮忙。

【问题讨论】:

  • 您目前面临哪些错误/问题? ...你的代码在哪里?
  • 这太疯狂了。对于如此大量的数据,您应该使用数据库。
  • 能否停止讨论数据库?我们不是客户。我们不提出要求。我们可以给出使用数据库的提示并且很好。如果客户不想要它,就是这样。按照客户想要的方式去做。
  • 正则表达式本身是很基础的,只是输入中具有正则表达式含义的转义和字符。然后将% 替换为.*,将_ 替换为.。然后遍历字符串并应用正则表达式,预计在得到结果之前会有相当长的延迟。
  • 例如for (String str : list) { /* match?? */ },正如我已经说过的那样,这可能需要一段时间......

标签: java android algorithm arraylist


【解决方案1】:

严重的不回答:考虑不要在您的应用中这样做。

当您处理如此大量的数据时,您真的不希望在(可能很便宜的)手机硬件上进行这种处理。迟早会有人在不合适的硬件上使用你的应用,然后就会抱怨“应用超级慢”。

含义:考虑在某种后端服务中这样做。然后,使用内置处理大量文本数据的技术,例如solr。所以,是的,应用程序可能会在您键入时向您的服务发送字符串,然后服务会发回建议、列表等。

【讨论】:

  • 非常感谢您的回答,但我无法提供后端服务,我必须在我的应用程序中执行此操作。顺便提一句。该应用程序仅供内部使用,没有客户。
  • @DanijelMarinkovic 内部客户可能会更糟。他们可以与他们的经理交谈,然后他们与您的经理交谈。事情就是这样:您的应用对其他人来说越有价值,他们就越依赖它。当它不工作时,你会被烤得越多(完美)。
【解决方案2】:

您可以尝试以下方法。首先,我们正在创建一个与您的搜索词匹配的正则表达式,然后我们按照 Korashen 的建议使用 paralellStream() 过滤列表。

正则表达式(?<=[_%])|(?=[_%]) 将在_% 处拆分字符串,同时保留分隔符。例如,如果您拆分 "A_B",则结果将是 ["A","_","B"] 而不是 ["A","B"]

private static List<String> filterByTerm(List<String> list, String term) {
    StringBuilder regexBuilder = new StringBuilder();
    String[] array = term.split("(?<=[_%])|(?=[_%])");
    for(String s : array) {
        switch(s) {
        case "_":
            regexBuilder.append(".");
            break;
        case "%":
            regexBuilder.append(".*");
            break;
        default:
            regexBuilder.append(Pattern.quote(s));
        }
    }

    String regex = regexBuilder.toString();
    return list.parallelStream().filter(s -> s.matches(regex)).collect(Collectors.toList());
}

经过验证的最小示例:

List<String> input = new ArrayList<>();
input.add("ABC");
input.add("AAC");
input.add("ACC");
input.add("ADC");

String term = "AB_";

filterByTerm(input, term).forEach(System.out::println);

给出ABC的输出。

【讨论】:

    【解决方案3】:

    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"));
    }
    

    【讨论】:

    • 很有趣,我真的很喜欢你使用树的想法。内存占用应该更大一点,但搜索速度更快。
    猜你喜欢
    • 1970-01-01
    • 2013-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多