Get enum by its inner field
具有内部字段的枚举,类似于映射。
现在,我需要通过其内部字段获取枚举。
写下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| package test;
/**
* Test enum to test enum =)
*/
public enum TestEnum {
ONE(1), TWO(2), THREE(3);
private int number;
TestEnum(int number) {
this.number = number;
}
public TestEnum findByKey(int i) {
TestEnum[] testEnums = TestEnum.values();
for (TestEnum testEnum : testEnums) {
if (testEnum.number == i) {
return testEnum;
}
}
return null;
}
} |
但是,每次我需要找到合适的实例时,通过所有枚举进行查找并不是很有效。
有没有其他方法可以做到这一点?
- 你能提供一些关于如何使用这个的更多的信息吗?
- @卡尔,我认为这是一个很常见的模式,使用了很多不同的方式。
- 您的findbykey()方法应该是静态的-否则您必须使用枚举对象调用该方法,如testenum.one.findbykey()而不是testenum.findbykey()。
您可以将static Map与static初始值设定项一起使用,该初始值设定项使用由其number字段键控的TestEnum值填充它。
注意,findByKey已制成static,number也已制成final。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| import java.util.*;
public enum TestEnum {
ONE (1), TWO (2), SIXTY_NINE (69);
private final int number ;
TestEnum (int number ) {
this. number = number ;
}
private static final Map <Integer,TestEnum > map ;
static {
map = new HashMap <Integer,TestEnum >();
for (TestEnum v : TestEnum. values()) {
map. put(v. number, v );
}
}
public static TestEnum findByKey (int i ) {
return map. get(i );
}
public static void main (String[] args ) {
System. out. println(TestEnum. findByKey(69)); // prints"SIXTY_NINE"
System. out. println(
TestEnum. values() == TestEnum. values()
); // prints"false"
}
} |
您现在可以期望findByKey是O(1)操作。
工具书类
- JLS 8.7静态初始值设定项
- JLS 8.9枚举
相关问题
- Java中的静态引用程序
- 如何在爪哇初始化静态地图
关于values()的说明
main方法中的第二条println语句显示:values()在每次调用时都返回一个新分配的数组!最初的O(N)解决方案只需调用values()一次并缓存数组,就可以做得更好一些,但该解决方案平均仍为O(N)。
- 好的解决方案。这也是我想到的。我通常在static { ... }内填充地图。
- 为什么懒惰?最简单的方法是在静态块中初始化映射。
- @彼得:增加了热切的版本。谢谢你的建议。我不太确定哪一种是比较常见的技术。
- 正如您所知道的,这个热切的版本在实践中无法工作(由于枚举是如何创建的)。实际上,您需要将该静态块嵌入到另一个嵌套的静态类中。
- 你发布的懒惰版本不是线程安全的。假设线程A和B都在map=null之前停止。A在返回前继续并停止。B执行map=并在初始化值之前停止。然后A继续,可以返回不正确的空值。尽管这不太可能,但对于多线程来说,代码仍然不正确。
- @Mjessup:很好的评论!是的,我根本没有考虑并发性。那么渴望的版本呢?为什么@james说它在实践中不起作用?我将在下一次修订中解决所有这些问题。
- 枚举父类在实例化子类之前初始化。因此,值数组通常在主枚举类的静态块中无效。如果这个静态块在另一个类中,那么没有问题。
- 嗯,经过进一步的调查,我有点不舒服。我想这只是一个问题,如果您试图访问枚举构造函数中的静态映射。我的错,这应该是可行的。
- R:"O(n)"。当n是一个可能变大的变量时,比较大操作系统是有用的。当n是枚举中常量的数目时,我确切知道有多少个常量,并且一个o(n^2)解决方案可能比其他人的o(logn)解决方案更好。
- @凯文:是的,但由于这个问题实际上需要一张地图,所以我想提出一个不同的解决方案:ala EnumMap,我们可以有一个专门的IntMiniMap implements Map,它有get(int)过载(防止自动氧化),并在屏幕后面进行线性搜索(或者根据n的阈值对排序的数组进行二进制搜索)。对于映射的线性搜索是很难看的,但是如果出于性能原因而必须这样做,那么至少要将其隐藏在一个专门的Map中,而不是强迫人们咬自己的舌头,然后在各处进行搜索。思想?
- 太棒了,但太可怕了,太可怕了,太可怕了!!!!(但不是你的错…):D+1
- 69是怎么出现的?
尽管有人建议使用Map,但还是要三思。
您最初的解决方案,特别是对于小的枚举,可能比使用哈希图更快。
在枚举包含至少30到40个元素之前,hashmap可能不会更快。
这是一个"如果它不坏,就不要修理它"的例子。
- 你凭什么认为江户十一〔十〕这么慢?它初始化一次,然后会比调用values()快得多(每次都必须是新分配的数组;尝试它TestEnum.values() == TestEnum.values()是false然后遍历每个元素。
- @聚原润滑油:我做过一次经验测试。看看HashMap.get的实现。它所做的远远超过了OP最初的实现。不久前我做了这个精确的测试,发现在至少有30个元素要遍历之前,考虑哈希图是不值得的。也就是说,您的实现是完全正确的,并且将完成一项工作。
- 我认为30-40可能是一个夸大,但我会尝试基准这一点。不过,我认为这个答案在本质上是正确的,对于这个数字的某些选择,需要指出。
- 我已经使用枚举中的10个常量完成了一个粗略的第一个基准测试。对于小的int值,我的MacBook上的性能是可比的——hashmap性能只比线性搜索差10%。使用随机的、较大的整数作为值,映射性能会变得更差,因为每个查询都需要分配一个整数实例。我在计时循环中测试了10个成功的查询和2个未找到的查询。这离最终的答案还很远,但至少应该说明亚历山大的答案并不是离题太远。我会在有机会的时候完成并发布基准代码。
- 哦!当然,在我的基准测试中,findbykey方法不调用values()。values()缓存在私有静态字段中。令人遗憾的是,values()被强制在每次调用时创建和填充一个全新的数组,因为它被声明为返回数组而不是集合类型。
一个解决方案是添加
1 2 3 4 5
| public final Test[] TESTS = { null, ONE, TWO, THREE };
public static Test getByNumber(int i) {
return TESTS[i];
} |
到枚举。
如果内部数据不是整数,则可以在static { ... }初始值设定项中填充映射。该图稍后可用于上述getByNumber方法中。
您应该有一个哈希映射,其中数字作为键,枚举值作为值。
此映射通常可以在您的存储库中。然后,您可以用首选的枚举值轻松地替换数据库中的int变量。
如果您的键(int值)存储在数据库中,那么在您的业务层的枚举中携带这些键是一种糟糕的设计。如果是这种情况,我建议不要在枚举中存储int值。
- 你能详细解释一下吗?对我来说,在Java端使用EDCOX1 0表示在数据库级别上用EDCOX1 OR 1表示的枚举是很自然的。为什么使用enum是一个糟糕的设计?你建议哪种选择?
- @帕斯卡:在分层应用程序中,您的服务代码不应该知道数据库中的键值。这不是层之间的松散耦合。如果您正在更改数据库或将其替换为某种类型的DataGrid(例如云解决方案),该怎么办?那么枚举中的数据库键就没有任何意义了。但是,如果您在集成逻辑中使用枚举,那么我会说PolyGene润滑剂Post是一个很好的解决方案。
- 如果密钥是业务密钥,而不仅仅是主键/外键,那么即使在服务层上使用,我也会投票支持PolyGene润滑剂解决方案。
- 谢谢。但是,如果您在持久层级别使用int,而在业务层级别使用enum,我仍然看不到问题。我看不出与携带钥匙的业务对象有什么区别。
- 如果您在枚举中携带了一个int值,而该值在将来的DataGrid解决方案中没有使用,或者如果您更改了数据库,而int值是错误的,那么您最多拥有一个难以读取的应用程序。您还可能面临这样的风险:有人对int值而不是枚举进行编码,因为它是可用的。