2012-01-16 76 views
4

此代码用于对列表进行排序。该列表可能包含数千个元素但少于10k。我被建议不要在这里使用反射......为什么不呢?

protected <E> int compareFields(E o1, E o2, String fieldName){ 
    try { 
     Comparable o1Data = (Comparable) o1.getClass().getMethod(fieldName).invoke(o1); 
     Comparable o2Data = (Comparable) o2.getClass().getMethod(fieldName).invoke(o2); 
     return o1Data == null ? o2Data == null ? 0 : 1 : 
       o2Data == null ? -1 : o1Data.compareTo(o2Data); 
    } catch(Exception e) { 
     throw new RuntimeException(e); 
    } 
} 

有人劝我

这样!! 要么用合适的比较,或者提取相关性的方法提供方法“请不要用的东西反射(可能以原始类型不支持的方式进行计算)或两者兼有。“

更好的方式来做到这将是很好的一个例子。

语境: 我有与数据表许多屏幕。每一个都是从List中构建的。每个数据表需要按其6列中的每一列进行排序。这些列是Date或String。

+1

什么是上下文?排序数据集? – 2012-01-16 19:25:36

+0

@Thomas Jungblut是的,我正在排序,名单可能在1000年。 – Dale 2012-01-16 19:27:04

+8

那么发布的评论已经告诉你该做什么。我完全同意:如果你可以改变代码(笔记的作者似乎也假设这一点),那么通过反射来做这件事不仅是低效但可怕的代码来维护(并且非常脆弱)。 – Voo 2012-01-16 19:29:42

回答

4

使用反射这里将可能要慢得多,因为你是通过使用getClassgetMethodinvoke而不是使用对象的原生比较方法加入堆栈帧到每个比较的数目。

理想情况下,你会写,以避免在签名中使用的object的方法。一个“合适的比较器”至少会强烈地绑定到对象的类型(你认为它是相同的)。如果您必须有动态字段比较(如其出现),那么至少反射可以封装在该比较器中。

如果你打算把这种上千次,不过,这将是最好预先绑定一个比较,以你被分拣场。这样,您只需要预先拨打getMethod,而不是每个单独的比较一次。

+0

OP正在比较对象的属性,它们是“Comparable”。如果需要按照一个以上的标准对它们进行分类,那么对象本身是否实现“Comparable”无关紧要。这个想法可能允许动态排序,例如在数据网格中。 – Groo 2012-01-16 19:32:51

+0

反映可能也必须做一些安全检查和什么。特别是在较老的JVM上,反射总体上非常缓慢,而且它变得越来越好,但它仍然很难对JIT进行优化(例如推测不是内联)。所以肯定是比一点慢一点。 – Voo 2012-01-16 19:33:11

+0

@格罗,谢谢,相应更新。 – harpo 2012-01-16 19:37:42

1

很难给出一个很好的例子,没有上下文,所以现在这里就是为什么它不是最好的主意,一个小清单:

  1. 提供的字段没有任何的是类似的担保(不知道为什么代码这里需要抓住这个例外并重新命名它)。
  2. 如果提供什么对象的类型并不意味着以这种方式进行比较? (这是一个过于通用的方法名称,知道它应该如何使用)。
  3. 它没有强类型。将字段名称设置为字符串意味着只要属性名称发生更改,就必须在各处更改代码,并且很难找到需要进行这些更改的位置。
  4. 反射比以强类型方式实现时可能慢。
1

其他答案很好地描述了为什么不建议使用反射。我想添加一个使用更传统的解决方案的例子。

而不是指定用于比较两个对象的字段,您应该将Comparator实例作为参数。这样使用此方法的客户端可以指定如何比较这两个对象。

protected <E> int compareFields(E o1, E o2, Comparator<E> comparator) { 
    return comparator.compare(o1, o2); 
} 

和实例调用这个函数应该是这样的:

MyClass a = ...; 
MyClass b = ...; 
Comparator<MyClass> intFieldComparator = new Comparator<MyClass> { 
    public int compare(MyClass o1, MyClass o2) { 
     int field1 = o1.getIntField(); 
     int field2 = o2.getIntField(); 

     return field2 - field1; 
    } 
}; 

compareFields(a, b, intFieldComparator); 

如果要比较使用多个字段对象可以定义不同的比较。

相关问题