我需要一种快速搜索子字符串的方法

时间:2017-11-07 14:51:30

标签: java algorithm search

我正在重新构建一个框架,我需要一个快速的算法来搜索字符串集合中的子字符串。

简而言之,当触发子关联的任何事件时,会提醒班级。

该事件包含一个路径,该路径是从当前类到触发事件的路径(通常是属性更改)。

每个类都具有对集合中加载的路径的静态绑定。 绑定由实际路径和绑定到所述路径的一组属性名称组成。

当一个类收到一个事件时,它需要检查是否有任何属性名称绑定到事件的路径并触发任何具有绑定的属性上的内容。

现在,我只是寻找最佳的集合类型来存储这些绑定,以及在静态绑定中搜索事件路径的最佳方法。

现在我的实施非常基础。我使用的是HashMap,键是可能的路径,而值是绑定到路径的一组属性。

我循环遍历键集,并使用startsWith和事件的路径。 (事件的路径需要是从索引0开始的绑定的子字符串)

例如,路径看起来像这样:" association1.association2.propertyInAssociation2"或" association1.association2.association3"

绑定贴图会看起来像这样(实际上没有像这样初始化它只是一个例子):

HashMap<String, Set<String>> bindings = new HashMap<>();
{
    bindings.put("association1.association2.propertyInAssociation2", new HashSet<>());
    bindings.get("association1.association2.propertyInAssociation2").add("property1");
    bindings.get("association1.association2.propertyInAssociation2").add("property2");
    bindings.get("association1.association2.propertyInAssociation2").add("property3");

    bindings.put("association1.association2.association3.propertyInAssociation3", new HashSet<>());
    bindings.get("association1.association2.association3.propertyInAssociation3").add("property4");
    bindings.get("association1.association2.association3.propertyInAssociation3").add("property5");
    bindings.get("association1.association2.association3.propertyInAssociation3").add("property6");
    bindings.get("association1.association2.association3.propertyInAssociation3").add("property7");
}

因此,对于具有这些绑定的类,接收具有类似&#34; association1.association2.association3.propertyInAssociation3&#34;的路径的事件。或&#34; association1.association2.association3&#34; 两者都需要在property4,property5,property6和property7上触发一些东西。

就像我说的,我需要的是搜索哪些属性(如果有的话)绑定到事件路径的最有效方法。

我使用Java 8,所以我不介意使用lambda或其他任何可用的东西。 将绑定重新编写为任何其他格式的字符串集合并不是不可能的,如果它有帮助的话。

非常感谢!

4 个答案:

答案 0 :(得分:2)

既然你说

  

我循环遍历键集,并使用startsWith和事件的路径。 (事件的路径需要是从索引0开始的绑定的子字符串)

您应该考虑使用不同的数据结构。 HashMap提供了有效的全键查找,但它对部分键查找没有多大帮助。您可以考虑使用SortedMap,例如TreeMap。对于String个密钥,SortedMap.tailMap()SortedMap.subMap()会帮助您直接导航到您要查找的密钥(如果它们存在)。

当然,TreeMap中的插入,删除和全键查找效率低于HashMap(平均值);这是对密钥子字符串搜索效率更高的权衡。

答案 1 :(得分:1)

我建议使用Stream API方法:

String path = "association1.association2.association3";
List<Map.Entry<String, Set<String>>> result = 
  bindings.entrySet()
            .stream()
            .filter(e -> e.getKey().contains(path))
            .collect(Collectors.toList());

答案 2 :(得分:1)

我的建议是使用Parallel Stream或实现自己的Map。

这里的测试:

John(TreeMap)提出的解决方案

最好:6毫秒

String path = "association1.association2.association3";
            TreeMap<String, HashSet> bindings2 = new TreeMap<String, HashSet>(new Comparator<String>() {

                @Override
                public int compare(String o1, String o2) {
                    if (o1.equals(o2))
                        return 0;
                    if (o1.startsWith(o2))
                        return 1;
                    return -1;
                }
            });
            {
                bindings2.put("association1.association2.propertyInAssociation2", new HashSet<>());
                bindings2.get("association1.association2.propertyInAssociation2").add("property1");
                bindings2.get("association1.association2.propertyInAssociation2").add("property2");
                bindings2.get("association1.association2.propertyInAssociation2").add("property3");

                bindings2.put("association1.association2.association3.propertyInAssociation3", new HashSet<>());
                bindings2.get("association1.association2.association3.propertyInAssociation3").add("property4");
                bindings2.get("association1.association2.association3.propertyInAssociation3").add("property5");
                bindings2.get("association1.association2.association3.propertyInAssociation3").add("property6");
                bindings2.get("association1.association2.association3.propertyInAssociation3").add("property7");
            }

            // test 1
            long time = System.currentTimeMillis();
            Object result1 =  bindings2.tailMap(path).entrySet().stream().filter(e -> e.getKey().contains(path))
                    .collect(Collectors.toList());
            System.out.println(System.currentTimeMillis() - time);
            System.out.println(result1);

Stefan(Stream)提出的解决方案

最好的:16毫秒

HashMap<String, Set<String>> bindings = new HashMap<>();
            {
                bindings.put("association1.association2.propertyInAssociation2", new HashSet<>());
                bindings.get("association1.association2.propertyInAssociation2").add("property1");
                bindings.get("association1.association2.propertyInAssociation2").add("property2");
                bindings.get("association1.association2.propertyInAssociation2").add("property3");

                bindings.put("association1.association2.association3.propertyInAssociation3", new HashSet<>());
                bindings.get("association1.association2.association3.propertyInAssociation3").add("property4");
                bindings.get("association1.association2.association3.propertyInAssociation3").add("property5");
                bindings.get("association1.association2.association3.propertyInAssociation3").add("property6");
                bindings.get("association1.association2.association3.propertyInAssociation3").add("property7");
            }

            // test 1
            long time = System.currentTimeMillis();
            String path = "association1.association2.association3";
            List<Map.Entry<String, Set<String>>> result =  bindings.entrySet().stream()
                    .filter(e -> e.getKey().contains(path)).collect(Collectors.toList());

            System.out.println(System.currentTimeMillis() - time);

            result.forEach(System.out::println);

Me提出的解决方案(并行流)

最好:9毫秒

HashMap<String, Set<String>> bindings = new HashMap<>();
            {
                bindings.put("association1.association2.propertyInAssociation2", new HashSet<>());
                bindings.get("association1.association2.propertyInAssociation2").add("property1");
                bindings.get("association1.association2.propertyInAssociation2").add("property2");
                bindings.get("association1.association2.propertyInAssociation2").add("property3");

                bindings.put("association1.association2.association3.propertyInAssociation3", new HashSet<>());
                bindings.get("association1.association2.association3.propertyInAssociation3").add("property4");
                bindings.get("association1.association2.association3.propertyInAssociation3").add("property5");
                bindings.get("association1.association2.association3.propertyInAssociation3").add("property6");
                bindings.get("association1.association2.association3.propertyInAssociation3").add("property7");
            }
            // test 1
            long time = System.currentTimeMillis();
            String path = "association1.association2.association3";
            List<Map.Entry<String, Set<String>>> result = bindings.entrySet().stream().parallel()
                    .filter(e -> e.getKey().contains(path)).collect(Collectors.toList());

            System.out.println(System.currentTimeMillis() - time);

            result.forEach(System.out::println);

数据很少,测试不可靠。我个人更喜欢Fred提出的解决方案。

更新:根据Dodgy的建议,您可以使用JMH找到更正式的测试

https://github.com/venergiac/benchmark-jmh

git clone https://github.com/venergiac/benchmark-jmh.git
mvn install
java -jar target\benchmark-0.0.1-SNAPSHOT.jar

并且测试显示使用hashmap在并行流上有更好的吞吐量,但是我们应该在更正式的环境中执行这些测试,并且有更多的时间。

答案 3 :(得分:1)

感谢所有的回复,但我改变了我的方法。

我仍然会使用HashMap,而不是添加:

  • “association1.association2.property”

并尝试匹配我将添加的部分键:

  • “association1”
  • “association1.association2”
  • “association1.association2.property”

这样我可以有效地使用哈希,因为绑定是静态的,并且每个类类型只生成一次,所以更改生成算法根本没有性能成本。

再次感谢您的所有答案。