2010-03-17 24 views
55

Tôi nhìn dưới mui xe cho EnumSet.allOf và nó trông rất hiệu quả, đặc biệt là cho enums với ít hơn 64 giá trị.Enum.values ​​() vs EnumSet.allOf(). Cái nào thích hợp hơn?

Về cơ bản tất cả các bộ chia sẻ một mảng của tất cả các giá trị enum có thể và phần thông tin duy nhất khác là một bitmask trong trường hợp allOf được đặt trong một swoop.

Mặt khác Enum.values ​​() có vẻ là một chút ma thuật đen. Hơn nữa nó trả về một mảng, không phải là một bộ sưu tập, vì vậy trong nhiều trường hợp nó phải được trang trí với Arrays.asList() để có thể sử dụng được ở bất kỳ nơi nào mong đợi bộ sưu tập.

Vì vậy, nên EnumSet.allOf thích hợp hơn là Enum.values?

Cụ thể hơn, trong đó hình thức for iterator nên được sử dụng:

for (final MyEnum val: MyEnum.values()); 

hoặc

for (final MyEnum val: EnumSet.allOf(MyEnum.class)); 

Trả lời

83

Vì tôi không nhận được câu trả lời cho câu hỏi của mình về câu hỏi nào hiệu quả hơn, tôi đã quyết định làm một số thử nghiệm của riêng mình.

Tôi đã thử nghiệm lặp lại trên values(), Arrays.asList(values())EnumSet.allOf(). Tôi đã lặp lại các thử nghiệm này 10.000.000 lần cho các kích thước enum khác nhau. Dưới đây là kết quả kiểm tra:

oneValueEnum_testValues   1.328 
oneValueEnum_testList   1.687 
oneValueEnum_testEnumSet  0.578 

TwoValuesEnum_testValues  1.360 
TwoValuesEnum_testList   1.906 
TwoValuesEnum_testEnumSet  0.797 

ThreeValuesEnum_testValues  1.343 
ThreeValuesEnum_testList  2.141 
ThreeValuesEnum_testEnumSet  1.000 

FourValuesEnum_testValues  1.375 
FourValuesEnum_testList   2.359 
FourValuesEnum_testEnumSet  1.219 

TenValuesEnum_testValues  1.453 
TenValuesEnum_testList   3.531 
TenValuesEnum_testEnumSet  2.485 

TwentyValuesEnum_testValues  1.656 
TwentyValuesEnum_testList  5.578 
TwentyValuesEnum_testEnumSet 4.750 

FortyValuesEnum_testValues  2.016 
FortyValuesEnum_testList  9.703 
FortyValuesEnum_testEnumSet  9.266 

Đây là kết quả cho các thử nghiệm chạy từ dòng lệnh. Khi tôi chạy các thử nghiệm này từ Eclipse, tôi đã nhận được sự hỗ trợ áp đảo cho testValues. Về cơ bản nó nhỏ hơn EnumSet ngay cả đối với enums nhỏ. Tôi tin rằng hiệu suất đạt được đến từ việc tối ưu hóa vòng lặp mảng trong vòng lặp for (val : array). Mặt khác, ngay khi bạn cần một java.util.Collection để vượt qua xung quanh, Arrays.asList() thua qua EnumSet.allOf, đặc biệt là cho các enums nhỏ, mà tôi tin rằng sẽ là đa số trong bất kỳ codebase đã cho.

Vì vậy, tôi sẽ nói bạn nên sử dụng

for (final MyEnum val: MyEnum.values()) 

nhưng

Iterables.filter(
    EnumSet.allOf(MyEnum.class), 
    new Predicate<MyEnum>() {...} 
) 

Và chỉ sử dụng Arrays.asList(MyEnum.values()) nơi java.util.List là hoàn toàn cần thiết.

+2

@ alexander-pogrebnyak, đánh dấu nó là câu trả lời – GetUsername

+1

@GetUsername Không muốn làm điều đó mà không có phiếu bầu của ai đó: D –

+1

Câu trả lời hay nhất +1 – PiersyP

12

Bạn nên sử dụng cách tiếp cận đó là đơn giản nhất và rõ ràng nhất cho bạn. Hiệu suất không được xem xét trong hầu hết các trường hợp.

IMHO: cả hai tùy chọn đều hoạt động không tốt vì cả hai đều tạo đối tượng. Một trong trường hợp đầu tiên và ba trong lần thứ hai. Bạn có thể tạo một hằng số chứa tất cả các giá trị vì lý do hiệu suất.

+10

Tạo ba đối tượng như một xem xét hiệu suất? Mate, không phải năm 1995 nữa ... –

+8

Năm 2010, và việc tạo một đối tượng vẫn không miễn phí. Đối với hầu hết các chương trình, việc tạo các đối tượng không quan trọng, nhưng nếu hiệu suất thực sự quan trọng, số lượng đối tượng bạn tạo có thể tạo ra sự khác biệt. –

+2

Tôi đã làm việc trên một dự án mà mỗi đối tượng được tạo ra trong chi phí đường dẫn tới hơn 200 đô la mỗi năm. Vì vậy, ba đối tượng có thể âm thanh đắt tiền trong một số bối cảnh, đặc biệt là nếu bạn làm điều này nhiều hơn một lần. –

4

Phương thức values() rõ ràng hơn và hiệu quả hơn nếu bạn chỉ muốn lặp qua tất cả các giá trị enum có thể có. Các giá trị được lớp lưu trong bộ nhớ cache (xem Class.getEnumConstants())

Nếu bạn cần một tập con các giá trị, bạn nên sử dụng EnumSet. Bắt đầu với allOf() hoặc noneOf() và thêm hoặc xóa các giá trị hoặc chỉ sử dụng of() nếu bạn cần.

+1

Các 'giá trị()' không thể được lưu trữ bởi lớp, bởi vì nó là một mảng và không có gì sẽ ngăn cản người dùng thay đổi các giá trị của nó. Do đó, tôi nghi ngờ nó phải là một bản sao. 'EnumSet.allOf', mặt khác sử dụng một giá trị chia sẻ cho mảng, do đó chắc chắn có ít phân bổ bộ nhớ ở đây. Vì vậy, 'giá trị' có thể rõ ràng hơn, nhưng tôi nghi ngờ nó không có hiệu quả hơn. –

+1

@Alexander: Bạn nói đúng, mảng được sao chép, nhưng bản sao() là bản địa. Một chút sửa lỗi cho tôi thấy rằng getEnumConstants() sử dụng các giá trị() không phải là cách khác. –

2

Không phải là tôi đã trải qua toàn bộ quá trình triển khai, nhưng dường như với tôi rằng EnumSet.allOf() về cơ bản đang sử dụng cùng cơ sở hạ tầng như .values ​​(). Vì vậy, tôi mong đợi EnumSet.allOf() yêu cầu một số (có thể không đáng kể) các bước bổ sung (xem http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6276988).

Có vẻ như rõ ràng với tôi rằng mục đích sử dụng dự kiến ​​là for(MyEnum val : MyEnum.values()) tại sao lại khác? Bạn sẽ chỉ nhầm lẫn lập trình bảo trì.

Ý tôi là, nếu bạn cần một bộ sưu tập, bạn sẽ nhận được một bộ sưu tập. Nếu bạn muốn sử dụng một foreach, mảng là đủ tốt. Tôi thậm chí còn thích mảng nếu ép! Tại sao bọc bất cứ thứ gì với bất cứ điều gì, nếu những gì bạn có (mảng) là đủ tốt? Những điều đơn giản thường nhanh hơn.

Dù sao thì, Peter Lawrey cũng đúng. Đừng bận tâm về hiệu suất của nó .. Nó đủ nhanh, và rất có thể có hàng triệu tắc nghẽn khác khiến cho sự khác biệt về hiệu suất lý thuyết nhỏ bé này hoàn toàn không liên quan (Đừng nhìn thấy điểm tạo đối tượng của anh ta). ví dụ có vẻ là 100% OK).

+0

@Zwei: xem nhận xét của tôi về bài đăng của Arne –

+0

@Alexander: OK, vậy là họ đã sửa lỗi (xem liên kết) trong JDK6? Vâng, tôi thấy quan điểm của bạn sau đó, nhưng tôi vẫn duy trì câu trả lời cho câu hỏi của bạn "Cụ thể hơn, hình thức cho trình lặp nào nên được sử dụng" như "sử dụng ví dụ đầu tiên". Ý tôi là, tôi không biết. Nếu bạn đang thực hiện một số phát triển trong ứng dụng thời gian thực được nhúng. hoặc một cái gì đó, có lẽ nó là chính đáng. Nhưng trong một bối cảnh chung, bình thường? Số –

7

Cũng Class.getEnumConstants()

là dưới mui xe tất cả họ đều gọi values() phương pháp loại enum dù sao, qua phản ánh.

+0

Điều này liên quan đến câu hỏi tôi đã hỏi như thế nào? –

+2

Điều này liên quan đến câu hỏi, bởi vì tất cả những người khác sử dụng các giá trị() dưới mui xe. –

0

EnumSet không được xây dựng với ý định lặp lại các giá trị của nó. Thay vào đó nó được thực hiện với ý tưởng cho nó để đại diện cho một BitMap hoặc BitMask hiệu quả (hoặc hợp lý hiệu quả). Các số javadoc on EnumSet cũng nêu rõ:

Các bộ Enum được biểu diễn bên trong dưới dạng vectơ bit. Biểu diễn này cực kỳ nhỏ gọn và hiệu quả. Hiệu suất không gian và thời gian của lớp này phải đủ tốt để cho phép sử dụng nó như là một sự thay thế an toàn, chất lượng cao cho các cờ bit "int" truyền thống. Ngay cả các hoạt động hàng loạt (chẳng hạn như containsAll và retainAll) sẽ chạy rất nhanh nếu đối số của chúng cũng là một tập hợp enum.

Vì chỉ một bit có thể đại diện cho một giá trị Enum nhất định, nó cũng được triển khai dưới dạng Set chứ không phải là List. Bây giờ, có lẽ bạn cũng có thể thực hiện tương tự, nhanh hơn, bằng cách sử dụng mặt nạ bit kiểu C (x^2), tuy nhiên nó cung cấp kiểu mã hóa trực quan hơn và sử dụng an toàn khi sử dụng enums, và nó mở rộng dễ dàng vượt quá kích thước của những gì mà int hoặc long có thể chứa.

Như vậy bạn có thể kiểm tra tất cả các bit được thiết lập như sau:

public class App { 
    enum T {A,B} 
    public static void main(String [] args) { 
    EnumSet<T> t = EnumSet.of(T.A); 
    t.containsAll(EnumSet.allOf(T.class)); 
    } 
} 
+0

Bạn nhận được nó hoàn toàn ngược. 'EnumSet' đầu tiên và trước hết là một' Bộ sưu tập', thực sự là một 'Bộ'. Bởi vì các thuộc tính của enums nó chỉ ra rằng đại diện hiệu quả nhất của một bộ như vậy là một mặt nạ bit. Ngoài ra, lưu ý rằng 'containsAll' trong ví dụ của bạn không phải là duy nhất cho' EnumSet', nó là một phương thức từ 'Set'. Nhưng điều này sang một bên, bạn đã không trả lời câu hỏi được đăng ban đầu, biểu mẫu nào hiệu quả hơn khi bạn cần truy cập vào tất cả các giá trị trong enum. –

+0

@AlexanderPogrebnyak chứng minh những gì tôi có chính xác lạc hậu vì nó không rõ ràng với tôi. Tôi chưa bao giờ thực hiện bất kỳ tuyên bố nào về 'EnumSet' là bất cứ thứ gì khác ngoài 'Set'. Do đó, có một cách tự nhiên rằng các phương thức như 'containsAll' và' retainAll' không phải là duy nhất cho 'EnumSet', mặc dù chúng có triển khai hoàn toàn độc đáo. Theo như câu trả lời, dường như bạn đã cung cấp một câu trả lời tuyệt vời với các chỉ số. Tôi chỉ dự định bổ sung bởi vì tôi không tin rằng sự lựa chọn nên tôi thực hiện chỉ dựa trên một sự lặp lại trên tập hợp đầy đủ các giá trị enum. – YoYo

+0

Đôi khi lặp lại toàn bộ các giá trị enum là lựa chọn duy nhất bạn có. Ví dụ: khi bạn phải xác thực và gán giá trị được truyền bên ngoài cho enum, và giá trị đó không rõ ràng ánh xạ tới tên enum, do đó bạn không thể sử dụng 'Enum.valueOf'. –

Các vấn đề liên quan