1

I need to have a relatively large number of categories defined (about 30 at start, we'll be adding more). Consider this code:

public class Category {

    public static final Category DRESS = new Category("Dress");
    public static final Category SKIRT = new Category("Skirt");
    ...

    private static final List<Category> CATEGORIES = Arrays.aslist(DRESS, SKIRT, ...);

    private String name;

    public Category(String name) {
         this.name = name;
    }

    //Some static public method to iterate over categories
   ... 

I need to have the categories declared and also need a way to iterate over them. I discard reflection because I think it's not a very good practice.

Is declaring a large name of static final fields of the same class and also having them inside a list a good practice? As an alternative, I thought about having a Map<Integer, Category> instead the list, and the fields were integers that would identify each category, so you would get the categories by getting them inside the map. Would this be better in terms of time and space performance?

PS: It's for an android project, if it changes something

jacosro
  • 509
  • 1
  • 5
  • 21
  • 1
    Sounds like a job for an `enum`. – Kevin Krumwiede Oct 25 '17 at 08:22
  • Using Enum should be avoided in Android I suppose. Though you are right. – vader Oct 25 '17 at 08:24
  • I think ישו אוהב אותך 's answer is the best option. I don't know about performance, but the enum approach gives you much cleaner code and makes your life that much better. I think that is worth any minor perf overhead if it has one. – ShahiM Oct 25 '17 at 09:32
  • and as @EpicPandaForce mentioned, `Category.values()` returns the elements as an array. – ShahiM Oct 25 '17 at 09:34
  • Enums have bad memory performance on android for large sets, try to go with a map or list instead. It's not going to be a deal breaker because you only mention one, , but they're considered bad practice. – Fabio Oct 25 '17 at 10:40
  • Well, actually they present the same problem as static fields... they will be loaded in memory whether you're using them or not. No garbage collection can clean them. – Fabio Oct 25 '17 at 10:41
  • @Fabio: Enum is considered bad practice, *previously*. We can found about it in the old documentation of performance at https://developer.android.com/training/articles/perf-tips.html. But there is no entry for enum there anymore. – ישו אוהב אותך Oct 25 '17 at 18:40

4 Answers4

3

Consider this code:

public class Category {

    public static final Category DRESS = new Category("Dress");
    public static final Category SKIRT = new Category("Skirt");

Yeah this is literally what enums do in the background, so

public enum Category {
    DRESS("Dress"),
    SKIRT("Skirt"),
    ...;

    private String name;

    private Category(String name) {
         this.name = name;
    }

    // Category.values() returns the elements as an array
EpicPandaForce
  • 79,669
  • 27
  • 256
  • 428
2

You should use enum instead of creating an object with new Category("Dress"); because creating an object is expensive than using enum. Java enums are implemented more like classes, so you can change your code seamlessly:

public enum Category {
  DRESS("Dress"), SKIRT("Skirt");

  private final String name;

  Category(String name) { 
    this.name = name; 
  }

  public String getName() { 
    return name; 
  }
}

Note:
The constructor for an enum type must be package-private or private access. It automatically creates the constants that are defined at the beginning of the enum body. You cannot invoke an enum constructor yourself.

Read more about enum at Enum Types

ישו אוהב אותך
  • 28,609
  • 11
  • 78
  • 96
1

I would say using a List is good enough.

You should consider a Map only if you have to look up a particular Category very frequently via some key property (like an int your case).

If There are no properties or methods in the Category class consider replace them with just Strings.

If new Categories are created at runtime and you want to persist them consider using a DB or File to save the Categories.

Edit: Answering the question in the comment

That would depend on the Category class. If its only purpose is to enumerate all the categories and the class itself does not have any other instance methods or properties then in terms of space complexity an Integer and your Category class is similar (since in a Map integer will be boxed in the Integer class object)

I would still suggest that you use a class called Category and a list if the purpose is only iterating over them and/or using specific instances of the Category class elsewhere in your application eg. Category.SOME_CATEGORY.

The following example is a good use-case

someMethod(Category category) {
    // do something
}

versus

someMethod(int category) {
   // before doing anything
   // lookup this category by an int key
   // in the the Map<Integer, Category>
}

The problem with the latter is that you could pass any int which may or may not be a valid key for a category. Using a class gives some bit for extra compile time check. Though you could always use an int def too. But again I would repeat that it all boils down to whether Category class has any instance methods or properties.

vader
  • 889
  • 8
  • 22
  • The purpose of adding a map is that the fields would took up less space, since they would be integers instead of Category objects. I'm asking if this would be correct and if it would be worth – jacosro Oct 25 '17 at 09:13
0

For small list, it is okay to use List or Map.

But for a large list, you may want to store them in a database.

Also ArrayList of String will be slightly efficient than using ArrayList of Category

Nabin Bhandari
  • 15,949
  • 6
  • 45
  • 59