自定义速度 Java 类 对比地图

Speed of custom Java classes vs. Maps

哪个更快:

  1. a Set 的每个元素都是 CustomObject 的一个实例,定义如下:

    public class CustomObject{
    
        private static String A;
        private static String B;
        private static String C;
    
        public static String getA(){
            return A;
        }
    
        public static getB(){
            return B;
        }
    
        public static getC(){
            return C;
        }
    }
    
  2. a Set 的每个元素都是 Map<String, String> 的一个实例,其中每个 Map 的格式为 {"A" : Avalue, "B" : Bvalue, "C" : Cvalue}?

  3. 或您能想到的任何其他数据结构可以更好地捕获此数据

如果我想得到所有 objects/maps 的子集 attribute/key A == "somevalue"?

您可以使用 .filter 或任何其他库。另外,如果集合很大,答案会改变吗?

编辑:我刚刚让我的一位同事告诉我自定义 class 的运行时间比散列图快——为什么有人会使用那么哈希图呢?

如果您在反复搜索哪些集合包含特定属性的哪些值时寻找最佳运行时性能,那么您要么需要使用数据库(或内存中的数据库),要么手动对数据进行非规范化,以便您维护每个属性的列表映射,以其值作为键。 请注意,只有当这些查找足够频繁并且性能足够关键以保证数据复制成本时,此解决方案才有价值。

例如

// aSets is a map to a list of all the sets which have
// a specified value for field "_A" (values being the map key)
Map<String, List<Set<MyObject>>> aSets;
Map<String, List<Set<MyObject>>> bSets;
...

它并不理想,但它提供了您需要的查找。

编写索引器 class 并不太难,它使用对提供的集合的基础对象的反射自动创建这些映射。我不久前写了一些代码来做这样的事情,作为某件事或其他事情的概念证明。从未使用过它,但保留了它...这是未注释的,使用风险自负(并且它不适用于所写的集合,但很容易修改):

public class Indexer
{
    private Map<String,Map<Object,Set<Object>>> index = new HashMap<String,Map<Object,Set<Object>>>();

    public void add(Object object) throws Exception
    {
        BeanInfo info = Introspector.getBeanInfo(object.getClass());

        PropertyDescriptor[] propertyDescriptors = info.getPropertyDescriptors();
        for (PropertyDescriptor descriptor : propertyDescriptors)
        {
            String fieldName = descriptor.getName();
            Map<Object,Set<Object>> map = index.get(fieldName);
            if (map == null)
            {
                map = new HashMap<Object,Set<Object>>();
                index.put(fieldName, map);
            }
            Method method = descriptor.getReadMethod();
            Object data = method.invoke(object);
            Set<Object> set = map.get(data);
            if (set == null)
            {
                set = new HashSet<Object>();
                map.put(data, set);
            }
            set.add(object);
        }

    }

    public Set<Object> get(String fieldName, Object value)
    {
        Map<Object,Set<Object>> map = index.get(fieldName);
        if (map != null)
        {
            Set<Object> set = map.get(value);
            if (set != null)
            {
                return Collections.unmodifiableSet(set);
            }
        }

        return null;
    }


    public static class Test
    {
        private String value1;
        private String value2;
        public int number;

        public Test(String v1, String v2, int n)
        {
            value1 = v1;
            value2 = v2;
            number = n;
        }

        public String getValue1()
        {
            return value1;
        }

        public void setValue1(String value1)
        {
            this.value1 = value1;
        }

        public String getValue2()
        {
            return value2;
        }

        public void setValue2(String value2)
        {
            this.value2 = value2;
        }


        @Override
        public String toString()
        {
            return "Test [value1=" + value1 + ", value2=" + value2 + ", number=" + number + "]";
        }


    }

    public static void main(String[] args) throws Exception
    {
        Test test1 = new Test("blue", "dog", 5);
        Test test2 = new Test("blue", "cat", 10);
        Test test3 = new Test("green", "duck", 10);

        Indexer indexer = new Indexer();
        indexer.add(test1);
        indexer.add(test2);
        indexer.add(test3);

        System.out.println("get value1 = blue: \n" + indexer.get("value1", "blue"));
        System.out.println("\nget value1 = green: \n" + indexer.get("value1", "green"));
        System.out.println("\nget number = 5: \n" + indexer.get("number", 5));
        System.out.println("\nget number = 10: \n" + indexer.get("number", 10));

    }

}

我假设我们正在将 Map<String, String> 与等效的自定义类型进行比较,如下所示:

public class CustomObject {
    private String a, b, c;

    public CustomObject(String a, String b, String c) {
        this.a = a; this.b = b; this.c = c;
    }
    public String getA() { return a; }
    public String getB() { return b; }
    public String getC() { return c; }
}

如果我们比较的操作是 obj.getA()map.get("A"),那么自定义地图会更快,可能快 1 到 2 个数量级。是的……快了很多。

另一方面,如果我们将 CustomObject 个实例放入一组“混合类型”对象中,而我们对这些对象的字段一无所知,那么调用 getA 就变得更复杂了困难/昂贵,Map 解决方案当然更简单,也可能更快。 (这取决于您可以做出的假设。)


Also, does the answer change if the set is large?

没有。它不会显着改变性能特征。


Why would anyone ever use a hashmap, then?

更好/必须使用 Map 的用例是当可能的键集在编译时未知。这意味着您不能将 CustomClass 编写为带有手写源代码的常规 class。

事实上,在大多数情况下,应该根据两种方法的相对代码简单性和稳健性来决定您采用的方法。如果键是静态的,则 obj.getA() 方法不仅速度更快。它也更健壮,因为你不会不小心写出类似 map.get("a") 而不是 map.get("A") 的东西......这将 return 一个意想不到的 null 并可能导致 NPE .如果密钥是动态的/在编译时未知,则 map.get("A") 更简单并且可能更健壮。

Java class 更快。

我的一位同事告诉我自定义 class 的运行时间比散列图快——那么为什么有人会使用散列图呢?

因为您可能对存储动态生成的值感兴趣 and/or 事先不知道您的 class 将具有什么属性。