杰瑞科技汇

Java enum 如何高效转字符串?

Of course! Working with enums and Strings in Java is a very common task. Here’s a comprehensive guide covering the most important scenarios, from basic conversion to best practices.

Java enum 如何高效转字符串?-图1
(图片来源网络,侵删)

The Basics: Creating an Enum with a String Value

Often, you want an enum constant to have an associated string value, like a database ID or a user-friendly display name. You achieve this by adding a field to the enum and a constructor.

Example: Status Enum

Let's create an enum for different statuses, where each status has a corresponding code.

public enum Status {
    // The enum constants with their associated string values
    ACTIVE("Active"),
    INACTIVE("Inactive"),
    PENDING("Pending Approval"),
    SUSPENDED("Suspended");
    // A field to hold the string value
    private final String displayName;
    // The constructor is private and is called for each constant
    private Status(String displayName) {
        this.displayName = displayName;
    }
    // A public method to get the string value
    public String getDisplayName() {
        return displayName;
    }
}

How it works:

Java enum 如何高效转字符串?-图2
(图片来源网络,侵删)
  1. private final String displayName;: Declares a private final field to store the string. It's final because the value for each constant should not change.
  2. private Status(String displayName) { ... }: This is the private constructor. When you write ACTIVE("Active"), you are implicitly calling this constructor, passing "Active" as the argument. This happens once for each constant when the enum class is loaded.
  3. public String getDisplayName() { ... }: This is a simple getter method to access the string value from outside the enum.

Common Scenarios and How to Handle Them

Scenario 1: Get the String Value from an Enum Constant

This is the easiest part. You use the getter method you defined.

public class Main {
    public static void main(String[] args) {
        Status myStatus = Status.ACTIVE;
        // Get the associated string value
        String statusString = myStatus.getDisplayName();
        System.out.println("The status is: " + statusString); // Output: The status is: Active
        System.out.println("Pending's display name: " + Status.PENDING.getDisplayName()); // Output: Pending's display name: Pending Approval
    }
}

Scenario 2: Get an Enum Constant from a String (The Reverse)

This is a more complex and critical operation. There are two main ways to do this, and one is strongly preferred over the other.

Method A: The Safe and Recommended Way (Using a Map)

Manually looping through values() with a for loop or Stream works, but it's inefficient if you do it repeatedly. The best practice is to create a static Map for fast, O(1) lookup.

public enum Status {
    ACTIVE("Active"),
    INACTIVE("Inactive"),
    PENDING("Pending Approval"),
    SUSPENDED("Suspended");
    private final String displayName;
    private Status(String displayName) {
        this.displayName = displayName;
    }
    public String getDisplayName() {
        return displayName;
    }
    // --- Static map for reverse lookup ---
    private static final Map<String, Status> BY_DISPLAY_NAME = new HashMap<>();
    // This static block populates the map when the class is loaded.
    static {
        for (Status status : values()) {
            BY_DISPLAY_NAME.put(status.getDisplayName(), status);
        }
    }
    // The public method to get an enum from a string
    public static Status fromDisplayName(String displayName) {
        if (displayName == null) {
            return null; // Or throw an IllegalArgumentException
        }
        return BY_DISPLAY_NAME.get(displayName);
    }
}

Usage:

Java enum 如何高效转字符串?-图3
(图片来源网络,侵删)
public class Main {
    public static void main(String[] args) {
        String statusText = "Active";
        Status status = Status.fromDisplayName(statusText);
        if (status != null) {
            System.out.println("Found enum: " + status); // Output: Found enum: ACTIVE
            System.out.println("Its display name: " + status.getDisplayName()); // Output: Its display name: Active
        } else {
            System.out.println("No enum found for: " + statusText);
        }
        // Example with a non-existent value
        Status unknownStatus = Status.fromDisplayName("Unknown");
        System.out.println("Unknown status: " + unknownStatus); // Output: Unknown status: null
    }
}

Why is this the best way?

  • Performance: The lookup in a HashMap is extremely fast (constant time, O(1)), whereas a loop or stream search is linear time (O(n)), which gets slower as your enum grows.
  • Readability: Status.fromDisplayName("Active") is very clear about its intent.
  • Robustness: You can easily handle nulls or invalid inputs inside the fromDisplayName method.

Method B: The Simple Loop (For Simplicity or Smaller Enums)

If your enum is small and performance isn't critical, you can use a simple loop. This approach is more common in basic examples.

public enum Status {
    ACTIVE("Active"),
    INACTIVE("Inactive"),
    PENDING("Pending Approval"),
    SUSPENDED("Suspended");
    private final String displayName;
    private Status(String displayName) {
        this.displayName = displayName;
    }
    public String getDisplayName() {
        return displayName;
    }
    // Simple reverse lookup method using a stream
    public static Status fromDisplayName(String displayName) {
        if (displayName == null) {
            return null;
        }
        // Uses Java 8 Streams for a concise solution
        return Arrays.stream(values())
                     .filter(s -> s.getDisplayName().equals(displayName))
                     .findFirst()
                     .orElse(null); // Or .orElseThrow(() -> new IllegalArgumentException(...))
    }
}

Usage is identical to the Map version.


Best Practices and Important Considerations

Case-Insensitive Lookup

Often, you want to find an enum regardless of the case of the input string. The Map approach is perfect for this. Just normalize the input string before using it as a key.

// In the fromDisplayName method
public static Status fromDisplayNameIgnoreCase(String displayName) {
    if (displayName == null) {
        return null;
    }
    // Convert the input to uppercase (or lowercase) to match the keys in the map
    return BY_DISPLAY_NAME.get(displayName.toUpperCase());
}
// You would need to store the keys in your map in uppercase too:
// static { BY_DISPLAY_NAME.put(status.getDisplayName().toUpperCase(), status); }

Handling Invalid Strings (Throwing Exceptions)

Returning null is one way to handle an invalid string, but it can lead to NullPointerExceptions later. A more robust approach is to throw an exception.

public static Status fromDisplayName(String displayName) {
    if (displayName == null || displayName.trim().isEmpty()) {
        throw new IllegalArgumentException("Display name cannot be null or empty.");
    }
    Status status = BY_DISPLAY_NAME.get(displayName);
    if (status == null) {
        throw new IllegalArgumentException("No enum constant with display name '" + displayName + "'");
    }
    return status;
}

Using enum.name() vs. a Custom String Field

  • enum.name(): This is a built-in method that returns the constant name as a String (e.g., Status.ACTIVE.name() returns "ACTIVE"). It's derived directly from the code and is useful for programmatic identifiers, logging, or serialization.
  • Custom String Field (like displayName): This is a field you define yourself. It holds a separate, user-friendly value (e.g., "Active"). Use this for any value that will be displayed to a user, saved to a database, or used in configuration files.

Example of the difference:

Status s = Status.PENDING;
System.out.println(s.name());           // Output: PENDING (the constant name)
System.out.println(s.getDisplayName()); // Output: Pending Approval (our custom value)

Serialization and @EnumValue (JPA)

If you are using a framework like JPA/Hibernate to persist your enum to a database, you might want to map the custom string field directly to the database column.

You can use the @Enumerated and @Enumerated(EnumType.STRING) annotations, but they use the enum.name(). To use your custom field, you can use a library-provided annotation or a custom AttributeConverter.

Example with JPA and Hibernate's @EnumValue:

import org.hibernate.annotations.EnumValue;
public enum PaymentType {
    CREDIT_CARD("CC"),
    PAYPAL("PAL"),
    BANK_TRANSFER("BT");
    @EnumValue // This tells Hibernate to use the string value in the DB
    private final String code;
    private PaymentType(String code) {
        this.code = code;
    }
    public String getCode() {
        return code;
    }
}
// Then in your entity class
@Entity
public class Order {
    @Enumerated(EnumType.STRING) // Still needed to tell JPA it's an enum
    @Column(name = "payment_type")
    private PaymentType paymentType;
    // ...
}

This would save the value "CC" to the database for PaymentType.CREDIT_CARD.

Summary

Task Recommended Approach
Get String from Enum Add a private field and a public getter (e.g., getDisplayName()).
Get Enum from String (Safe) Use a static Map for O(1) lookup. This is the most performant and scalable solution.
Get Enum from String (Simple) Use a for loop or Stream over values(). Fine for small enums or quick scripts.
Case-Insensitive Lookup Normalize the input string (e.g., to uppercase) before using it as a key in your Map.
User-Friendly Display Use a custom string field (like displayName), not enum.name().
Persistence (e.g., JPA) Use annotations like @EnumValue (Hibernate) or an AttributeConverter to map your custom string field to the database.
分享:
扫描分享到社交APP
上一篇
下一篇