4

Disclaimer: I am kind of new to Java :)

I am running a bunch of selections on some data and in order to keep track of what happens at each stage of the the selection I use int counters. These counters are all in a data object:

public class MyCounters {
    private int counter0;
    private int counter1;
    ...
}

I also have to count how many candidates end up in a given number of categories, which I account for with an enum. To do this I created List<Integer> where the index of the list covers the values of the enum.

private List<Integer> myList;

And later in the code I need a dedicated method to initialise the list with zeros:

for (MyEnum i : MyEnum.values()) {
    myList.add(0);
}

In the main code then, once the final category has been assigned, this happens:

myCounters.getMyList().set(myEnum.ordinal(), myCounters.getList().get(myEnum.ordinal()) + 1);

I was suggested that the declaration/initialisation steps can be improved using Lombok's @Builder.Default functionality (or maybe @Singular), but I can't really find out how: in the end I need to initialise a List<Integer> to as many zeros as the values in the enum. Is it really possible to do this using Lombok's extensions? Or are they targeted for something different?

FrancescoLS
  • 376
  • 1
  • 6
  • 17
  • I don't know who gave you that advice, but it sounds like *bad* advice. I can't see how that's relevant. You're best off asking the person who told you that to give you an example, because it doesn't seem like it will help you. – Michael Dec 03 '18 at 13:57

3 Answers3

2

Lombok's @Builder + @Singular on their own will initialize your List with an empty ArrayList, and that's it (they won't initialize this List with any elements, like zeroes). @Builder.Default could do that (you don't need @Singular then), but I would not follow that path if possible.

I don't fully understand what you want to do, e.g. I don't know if you have only one enum (MyEnum), or if there's more than one enum.

If you have only MyEnum, you'd be much better off using a different data structure than List:

  1. An EnumMap is the easy choice, because it's native to Java:

    • initialization: EnumMap<MyEnum, Integer> myMap = new EnumMap<>(MyEnum.class)
    • incrementing: myMap.merge(myEnum, 1, Integer::sum)
    • final result: myMap.getOrDefault(myEnum, 0)
  2. The best data structure for this, though, would be a multiset. One external library that supports mulitsets is Guava with its Multiset:

    • initialization: Multiset<MyEnum> myMultiset= HashMultiset.create()
    • incrementing: myMultiset.add(myEnum)
    • final result: myMultiset.count(myEnum)
Tomasz Linkowski
  • 4,386
  • 23
  • 38
  • 1
    Thanks a lot! This isn't exactly what I was looking for but is definitely better than what I had in mind!! I used the `EnumMap` and it works fine (and solves another small problem I was trying to address) – FrancescoLS Dec 04 '18 at 09:15
2

Sticking to your architecture, I guess you have to initialize the List within a class that uses Lombok. That can be achieved as follows:

@Builder
public class Foo {

  @Builder.Default
  private List<Integer> myList = Arrays.asList(0, 0, 0);

}

Arrays.asList is how you can initialize a List with default values using the standard Java libraries. I know it can be a bit confusing to use a class called Arrays instead of List or Collection, but you can find more information on its Javadoc (here the doc for Java 8). The result of that initialization is a list with three Integers set to 0. You just need to put as many as you need.

The reason to use the annotation @Builder.Default on the myList field is to make the builder constructor aware of the default initialization that otherwise would be skipped by the Lombok's builder.

For brevity, I only included the very specific code for initializing your List and the builder. Note that probably you'd want to use also the Lombok annotations @Data and @AllArgsConstructor in combination with it.

You can find more information on the Lombok official documentation

Niccolò
  • 2,854
  • 4
  • 24
  • 38
  • Thanks a lot @Niccolò this is very useful and I guess it is indeed what I was trying to do (with no success)!! I see though that the number of elements wold be hardcoded and not set according to the elements in the enum, so I'll give a try to some of the other suggestions I received. And yes, I am already using `@Data` and other annotations :) – FrancescoLS Dec 04 '18 at 08:05
0

Honestly, I suggest a different architecture:

  • Consider not using Enum.ordinal(). It will work fine if you just care about "one" point in time, but if you persist your data somehow then things break apart as soon as you want to compare different persisted data (and the enum changed in the meantime)
  • Maybe consider LongAdder.

Meaning: use a Map<YourEnumType, LongAdder> to count things. Retrieve the counter, call its add() method, done.

GhostCat
  • 137,827
  • 25
  • 176
  • 248
  • 1
    thanks a lot for the comment! I am getting to know Java and every piece of info is useful! Nevertheless I decided to use an `EnumMap` as suggested by @TomaszLinkowski – FrancescoLS Dec 04 '18 at 09:19