JCS: Used Complexobject or String as key - java

I have a question about how .get(key) works.
If i use an complex object as a key, i figured out that the equals function is not called. But which reference the framework used to identify the key objects.
I need to know which one is faster? String or Object as key?

This is the get function, getCacheControl() is returning a IMemoryCache<K, V> implementation.
https://github.com/apache/commons-jcs/blob/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/CacheAccess.java
#Override
public V get( K name )
{
ICacheElement<K, V> element = this.getCacheControl().get( name );
return ( element != null ) ? element.getVal() : null;
}
AbstractMemoryCache implementing IMemoryCache and using a Map,
https://github.com/apache/commons-jcs/blob/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/AbstractMemoryCache.java
/** Map where items are stored by key. This is created by the concrete child class. */
public Map<K, MemoryElementDescriptor<K, V>>
So we can assume similar semantics. Composite objects should not make any difference, maybe you forgot to override hashCode. Map first uses it to find the bucket, later uses equals.

Related

Map containsKey returns false

My got a HashMap data:
Map<CharSequence, MyObject> dataMap = GET_FROM_SOME_WHERE
There is a key in the dataMap which is a CharSequence type with value "company.name"
But the following code returns me false:
String field = "company.name";
dataMap.containsKey(field); //This return me false
I somehow feel that it is because my field variable is a String object while the key in HashMap is CharSequence. That's why it returns me false.
If my guess is correct, then how to get rid of it? I need the above code returns me true. I am sure the key "company.name" is in that map data as key.
The Java API Spec has this to say on the subject:
'Each object may be implemented by a different class, and there is no guarantee that each class will be capable of testing its instances for equality with those of the other. It is therefore inappropriate to use arbitrary CharSequence instances as elements in a set or as keys in a map.'
why not just put
CharSequence field = "company.name";
Are you putting StringBuffers into a HashMap? Because that is not going to work, since StringBuffer does not define a hashCode method. It inherits hashCode from java.lang.Object which uses the object's identity as hash code.
String on the other hand calculates hash code from the actual string data.
Edit: StringBuffer doesn't implement equals() method either, so it won't work at all like one would expect. (new StringBuffer("1")).equals(new StringBuffer("1")) -> false.
In order for this to work, the implementation of CharSequence inside your map must recognize Strings in checking for equality, produce identical hash codes with the String, and return true when compared for equality to a string with the same value (i.e. equals should work from both sides). This is not possible unless the implementation of CharSequence is actually a String.
One way to address this is converting the Map<CharSequence,MyObject> to a Map<String,MyObject>. Iterate through the entry set of the original map, and put the data into a copy that uses the String as the key, like this:
Map<String,MyObject> copy = new HashMap<String,MyObject>();
for (Map.Entry<CharSequence,MyObject> e : dataMap.entrySet()) {
copy.put(e.getKey().toString(), e.getValue());
}
Using Collection can be quite tricky sometimes.
The way that a Map.containsKey() works is by examining object equality by calling the .equals() method of your 'key' objects. So, if you put things in the map via a StringBuffer - which also implements CharSequence - but try to ask whether the key exists in the map but providing a String as the key, you are really challenging the Collections framework.
This can be shown by below example:
Map<CharSequence, Integer> dataMap = new HashMap<CharSequence, Integer>();
StringBuffer sb = new StringBuffer();
sb.append("company.name");
CharSequence cs = sb;
dataMap.put(cs, 123);
String k = "company.name";
// Below prints a 'false'
System.out.println(dataMap.containsKey(k));
Accordingly, my advice is to always use the same type of objects as key entries in a collection (not just Map). In this case, perhaps you can also define your Map as
Map<String, MyObject>
Possible reason is that equals() method is used in your map. Another is hashCode() is used. If key in map isn't a String, it can give you another values by these methods.
If you are getting map somewhere outside and cannot control it, iteration over key set seems to be the only approach here. Otherwise I would suggest to change map to Map<String, MyObject>
public static void main(String[] args) {
Map<CharSequence, String> dataMap = new HashMap<>();
dataMap.put(new StringBuilder("company.name"), "AAA");
System.out.println(dataMap.containsKey("company.name"));
System.out.println(mapContainsKeyNamed("company.name", dataMap));
}
static boolean mapContainsKeyNamed(String key, Map<CharSequence, ?> map) {
for (CharSequence cs : map.keySet()) {
if (key.equals(cs.toString())) {
return true;
}
}
return false;
}
output:
false
true

Sort by one field and remove elements by another, in a Map?

Suppose there is NavigableMap<Key, Value> where Key would be:
class Key {
private String keySort;
private String keyRemove;
//getters, setters etc.
#Override
public boolean equals(Object o) {
//only include keyRemove
}
#Override
public int hashCode() {
//only include keyRemove
}
}
And there would also be a comparator:
class SortComparator implements Comparator<Key> {
#Override
public int compare(Key o1, Key o2) {
return o1.getKeySort().compareTo(o2.getKeySort());
}
}
Now the map instantiation would look like this: NavigableMap<Key, Value> myMap = new TreeMap<>(new SortComparator()); What I want is to have inside the Map, the entries sorted only by the keySort and to remove them only based on keyRemove.
The problem is that when I try to remove elements from the map, having only the keyRemove value, it does not work as expected (NullPointerException in Comparator). How can the above implementation be fixed in order to work with the expected behavior, or how can the expected behavior be implemented otherwise?
Edit: I understand what's wrong; the question remains still, for alternatives. From TreeMap JavaDoc:
Note that the ordering maintained by a sorted map (whether or not an
explicit comparator is provided) must be consistent with equals if
this sorted map is to correctly implement the Map interface. (See
Comparable or Comparator for a precise definition of consistent with
equals.)
I am thinking that when you want to remove an entry in a map, the remove method will do the job based on the equals method:
(key==null ? k==null : key.equals(k))
like described here http://docs.oracle.com/javase/6/docs/api/java/util/AbstractMap.html#remove%28java.lang.Object%29.
One solution would be to implement your own remove method by iterating through all the keys in the keyset - searching for the removeKey, but that that will replace the O(1) complexity with O(n). Maybe this can be an workaround for you.

Changing an object which is used as a Map key

Let V be a class with a single attribute named K and its getter and setters.
What's supposed to happen if I do:
V v = new V();
v.setK("a");
HashMap<K,V> map = new HashMap<K,V>();
map.put(v.getk(),v);
v.setK("b");
As far as I know, this should cause some kind of problem because a map key is supposed to be invariable. What would happen here?
Edit: Consider the key not to be a String but a mutable object as stated in the coment below.
Quote from "Map" interface JavaDoc:
Great care must be exercised if mutable objects are used as map keys. The behavior of a map is not specified if the value of an object is changed in a manner that affects equals comparisons while the object is a key in the map.
You simply shouldn't mutate keys (in the way which changes their "hashCode"/"equals"). You will definitely have very long and awful debugging if you try.
It's like you swap books in the library. Index became unreliable. You search for "Bradbury", but find "Simak".
If you were trying to look up the map using v.getk() it wouldn't find an entry, because you've changed the value return by the getter on v.
Or in other words, the map isn't magically kept in sync with what happens to your v object - it uses (and keeps using) the value given to it in the put().
V v = new V();
v.setK("a");
HashMap<K,V> map = new HashMap<K,V>();
map.put(v.getk(),v);
//Nothing will change in the hashmap with this step
v.setK("b");
but the problem will be while fetching the object V from map. If you call map.get(v.getk()), you will get null because the value in the map is mapped with object "a". However, since this String "a" is inter you can always fetch this object from map by map.get("a"); or
V v = new V();
v.setK("a");
map.get(v.getK());
PS: I have not tried to run this
The title of this question is misleading -- you are not changing the map key, as in mutating the object used as map key. When you say map.put(x, y), you are creating a map entry that aggregates two independent values: a key and a value. Where the key originates from is not seen by the map, it's just two objects. So, you have created a map entry ("a", v) and after that you just changed the state of v -- there is no way this could have influenced the map entry's key "a". If, on the other hand, you had an object K of your own making, like
public class K {
private String s;
public K(String s) { this.s = s; }
public void setS(String s) { this.s = s; }
public boolean equals(Object o) { return ((K)o).s.equals(this.s); }
public int hashCode() { return s.hashCode(); }
}
and now you do
final K k = new K("a");
map.put(k, v);
k.setS("b");
map.get(k);
then you would face the problem -- you mutated the object used as the map key.
By calling v.setK(), you aren't changing the key in the HashMap. So you will simply have wrong information in your V object.

How to create a custom iterator for a Map implementation?

I implemented a unique map. It's a hashmap which is bi-directional, where not only keys are unique but values too.
public interface UniqueMap<K,V>{
V uniquePut(K key, V value);
UniqueMap<V,K> inverse();
}
This is a possible implementation:
public class SimpleUniqueMap<K,V> implements UniqueMap<K,V>, Iterable<K>{
public HashMap<K,V> uniqueMap = new HashMap<K,V>();
class EnumSimpleUniqueMap implements Iterator<K>{
int count = uniqueMap.size();
public boolean hasNext(){
return count > 0;
}
public K next(){
if(count == 0){
throw new NoSuchElementException();
}else{
count--;
//...
}
}
public void remove(){
throw new UnsupportedOperationException();
}
}
public Iterator<V> iterator(){
return new EnumSimpleUniqueMap();
}
public V uniquePut(K key, V value){
return null;
}
public UniqueMap<V,K> inverse(){
return null;
}
}
As you can see I already tried to implement an iterator for my unique map. But from a hashmap values are not accessed by position but by key. So usually I would take the counter and access values but in this case it is not possibe.
Actually it would be enough to iterate over the keys and retrieve them one by another. How can I do that? Is there a way to retrieve some kind of entry object containing both key and value?
I know that I can retrieve the iterator from an map object but this is not an option for me.
UPDATE: Most simple of all, use
org.apache.commons.collections.BidiMap
But if your really want to roll your own, then consider this:
Usually, Maps don't implement Iterable. In your case, you can get the Iterator for free by calling any of these
map.keys().iterator(); // is the same as
map.inverse().values().iterator();
map.values().iterator(); // is the same as
map.inverse().keys().iterator();
map.entrySet().iterator(); // almost the same as
map.inverse().entrySet().iterator();
on your map, depending on what you want to iterate over. For that, you'd have to make
public interface UniqueMap<K,V> extends Map<K, V> {
// no need for uniquePut(), you already have Map.put()
UniqueMap<V,K> inverse();
}
It's also a good idea to make your implementation extend
java.util.AbstractMap<K, V>
Which has a lot of base functionality for maps already.
You could implement your iterator() method by simply delegating to your backing hashmap's keyset iterator:
public Iterator<K> iterator(){
return uniqueMap.keySet().iterator();
}
Of course, as Lukas said, usually a map will not be iterable, but provide collection views which themselves are iterable.
Also, it might be a good idea for your unique map implementation to have HashMaps in both directions.
Also, think about (and specify it in the interface): What should happen if the user inserts a new key with an already existing value - does this fail, get ignored, remove the existing mapping, or what?
You should have a look to the Guava library (Google Collection). They have a BiMap implementation that seems to be exactly what your are trying to implement...

A Java collection of value pairs? (tuples?)

I like how Java has a Map where you can define the types of each entry in the map, for example <String, Integer>.
What I'm looking for is a type of collection where each element in the collection is a pair of values. Each value in the pair can have its own type (like the String and Integer example above), which is defined at declaration time.
The collection will maintain its given order and will not treat one of the values as a unique key (as in a map).
Essentially I want to be able to define an ARRAY of type <String,Integer> or any other 2 types.
I realize that I can make a class with nothing but the 2 variables in it, but that seems overly verbose.
I also realize that I could use a 2D array, but because of the different types I need to use, I'd have to make them arrays of OBJECT, and then I'd have to cast all the time.
I only need to store pairs in the collection, so I only need two values per entry. Does something like this exist without going the class route? Thanks!
AbstractMap.SimpleEntry
Easy you are looking for this:
java.util.List<java.util.Map.Entry<String,Integer>> pairList= new java.util.ArrayList<>();
How can you fill it?
java.util.Map.Entry<String,Integer> pair1=new java.util.AbstractMap.SimpleEntry<>("Not Unique key1",1);
java.util.Map.Entry<String,Integer> pair2=new java.util.AbstractMap.SimpleEntry<>("Not Unique key2",2);
pairList.add(pair1);
pairList.add(pair2);
This simplifies to:
Entry<String,Integer> pair1=new SimpleEntry<>("Not Unique key1",1);
Entry<String,Integer> pair2=new SimpleEntry<>("Not Unique key2",2);
pairList.add(pair1);
pairList.add(pair2);
And, with the help of a createEntry method, can further reduce the verbosity to:
pairList.add(createEntry("Not Unique key1", 1));
pairList.add(createEntry("Not Unique key2", 2));
Since ArrayList isn't final, it can be subclassed to expose an of method (and the aforementioned createEntry method), resulting in the syntactically terse:
TupleList<java.util.Map.Entry<String,Integer>> pair = new TupleList<>();
pair.of("Not Unique key1", 1);
pair.of("Not Unique key2", 2);
The Pair class is one of those "gimme" generics examples that is easy enough to write on your own. For example, off the top of my head:
public class Pair<L,R> {
private final L left;
private final R right;
public Pair(L left, R right) {
assert left != null;
assert right != null;
this.left = left;
this.right = right;
}
public L getLeft() { return left; }
public R getRight() { return right; }
#Override
public int hashCode() { return left.hashCode() ^ right.hashCode(); }
#Override
public boolean equals(Object o) {
if (!(o instanceof Pair)) return false;
Pair pairo = (Pair) o;
return this.left.equals(pairo.getLeft()) &&
this.right.equals(pairo.getRight());
}
}
And yes, this exists in multiple places on the Net, with varying degrees of completeness and feature. (My example above is intended to be immutable.)
Java 9+
In Java 9, you can simply write: Map.entry(key, value)
to create an immutable pair.
Note: this method does not allow keys or values to be null. If you want to allow null values, for example, you'd want to change this to: Map.entry(key, Optional.ofNullable(value)).
Java 8+
In Java 8, you can use the more general-purpose javafx.util.Pair to create an immutable, serializable pair. This class does allow null keys and null values. (In Java 9, this class is included in the javafx.base module). EDIT: As of Java 11, JavaFX has been decoupled from the JDK, so you'd need the additional maven artifact org.openjfx:javafx-base.
Java 6+
In Java 6 and up, you can use the more verbose AbstractMap.SimpleImmutableEntry for an immutable pair, or AbstractMap.SimpleEntry for a pair whose value can be changed. These classes also allow null keys and null values, and are serializable.
Android
If you're writing for Android, just use Pair.create(key, value) to create an immutable pair.
Apache Commons
Apache Commons Lang provides the helpful Pair.of(key, value) to create an immutable, comparable, serializable pair.
Eclipse Collections
If you're using pairs that contain primitives, Eclipse Collections provides some very efficient primitive pair classes that will avoid all the inefficient auto-boxing and auto-unboxing.
For instance, you could use PrimitiveTuples.pair(int, int) to create an IntIntPair, or PrimitiveTuples.pair(float, long) to create a FloatLongPair.
Hand-rolled implementations
As of Java 16, records have come out of preview status, so you can now do:
public record Pair<K, V>(K key, V value) {
public static <K, V> Pair<K, V> of(K key, V value) {
return new Pair<>(key, value);
}
}
The above implementation will have a big advantage in the future, as it'll allow you to do record deconstruction.
Prior to Java 16, you can achieve the same semantics with Project Lombok:
#Value(staticConstructor = "of")
public class Pair<K, V> {
K key;
V value;
}
or, with the following verbosity (which, unlike the class listed in the accepted answer, guards against NullPointerExceptions, and has a robust hashCode() implementation identical to that of Records1):
import java.util.Objects;
public class Pair<K, V> {
public final K key;
public final V value;
private Pair(K key, V value) {
this.key = key;
this.value = value;
}
public static <K, V> Pair<K, V> of(K key, V value) {
return new Pair<>(key, value);
}
public boolean equals(Object o) {
return o instanceof Pair && Objects.equals(key, ((Pair<?,?>)o).key) && Objects.equals(value, ((Pair<?,?>)o).value);
}
public int hashCode() {
return 31 * Objects.hashCode(key) + Objects.hashCode(value);
}
public String toString() {
return key + "=" + value;
}
}
1 Tested on OpenJDK 17
Map.Entry
These built-in classes are an option, too. Both implement the Map.Entry interface.
AbstractMap.SimpleEntry
AbstractMap.SimpleImmutableEntry
Apache common lang3 has Pair class and few other libraries mentioned in this thread What is the equivalent of the C++ Pair<L,R> in Java?
Example matching the requirement from your original question:
List<Pair<String, Integer>> myPairs = new ArrayList<Pair<String, Integer>>();
myPairs.add(Pair.of("val1", 11));
myPairs.add(Pair.of("val2", 17));
//...
for(Pair<String, Integer> pair : myPairs) {
//following two lines are equivalent... whichever is easier for you...
System.out.println(pair.getLeft() + ": " + pair.getRight());
System.out.println(pair.getKey() + ": " + pair.getValue());
}
To anyone developing for Android, you can use android.util.Pair. :)
What about "Apache Commons Lang 3" Pair class and the relative subclasses ?
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
...
#SuppressWarnings("unchecked")
Pair<String, Integer>[] arr = new ImmutablePair[]{
ImmutablePair.of("A", 1),
ImmutablePair.of("B", 2)};
// both access the 'left' part
String key = arr[0].getKey();
String left = arr[0].getLeft();
// both access the 'right' part
Integer value = arr[0].getValue();
Integer right = arr[0].getRight();
ImmutablePair is a specific subclass that does not allow the values in the pair to be modified, but there are others implementations with different semantic. These are the Maven coordinates, if you need them.
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
You could write a generic Pair<A, B> class and use this in an array or list. Yes, you have to write a class, but you can reuse the same class for all types, so you only have to do it once.
Java 14+ edition
You can create a record which implements equals, hashCode, and toString out of the box. Interfaces like Comparable could also be implemented, if needed.
record Pair<A, B>(A first, B second) {}
Records are immutable.
Expanding on the other answers a generic immutable Pair should have a static method to avoid cluttering your code with the call to the constructor:
class Pair<L,R> {
final L left;
final R right;
public Pair(L left, R right) {
this.left = left;
this.right = right;
}
static <L,R> Pair<L,R> of(L left, R right){
return new Pair<L,R>(left, right);
}
}
if you name the static method "of" or "pairOf" the code becomes fluent as you can write either:
list.add(Pair.of(x,y)); // my preference
list.add(pairOf(x,y)); // use with import static x.y.Pair.pairOf
its a real shame that the core java libraries are so sparse on such things that you have to use commons-lang or other 3rd parties to do such basic stuff. yet another reason to upgrade to scala...
The preferred solution as you've described it is a List of Pairs (i.e. List).
To accomplish this you would create a Pair class for use in your collection. This is a useful utility class to add to your code base.
The closest class in the Sun JDK providing functionality similar to a typical Pair class is AbstractMap.SimpleEntry. You could use this class rather than creating your own Pair class, though you would have to live with some awkward restrictions and I think most people would frown on this as not really the intended role of SimpleEntry. For example SimpleEntry has no "setKey()" method and no default constructor, so you may find it too limiting.
Bear in mind that Collections are designed to contain elements of a single type. Related utility interfaces such as Map are not actually Collections (i.e. Map does not implement the Collection interface). A Pair would not implement the Collection interface either but is obviously a useful class in building larger data structures.
I was going to ask if you would not want to just use a List<Pair<T, U>>? but then, of course, the JDK doesn't have a Pair<> class. But a quick Google found one on both Wikipedia, and forums.sun.com. Cheers
Spring has a Pair<S,T> type in the Data Utils package org.springframework.data.util
Pair<String,Integer> pair = Pair.of("Test", 123);
System.out.println(pair.getFirst());
System.out.println(pair.getSecond());
This is based on JavaHelp4u 's code.
Less verbose and shows how to do in one line and how to loop over things.
//======> Imports
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
//======> Single Entry
SimpleEntry<String, String> myEntry = new SimpleEntry<String, String>("ID", "Text");
System.out.println("key: " + myEntry.getKey() + " value:" + myEntry.getValue());
System.out.println();
//======> List of Entries
List<Entry<String,String>> pairList = new ArrayList<>();
//-- Specify manually
Entry<String,String> firstButton = new SimpleEntry<String, String>("Red ", "Way out");
pairList.add(firstButton);
//-- one liner:
pairList.add(new SimpleEntry<String,String>("Gray", "Alternate route")); //Ananomous add.
//-- Iterate over Entry array:
for (Entry<String, String> entr : pairList) {
System.out.println("Button: " + entr.getKey() + " Label: " + entr.getValue());
}
Apache Crunch also has a Pair class:
http://crunch.apache.org/apidocs/0.5.0/org/apache/crunch/Pair.html
I mean, even though there is no Pair class in Java there is something pretty simmilar: Map.Entry
Map.Entry Documentation
This is (simplifying quite a bit) what HashMap , or actually any Map stores.
You can create an instance of Map store your values in it and get the entry set. You will end up with a Set<Map.Entry<K,V>> which effectively is what you want.
So:
public static void main(String []args)
{
HashMap<String, Integer> values = new HashMap<String,Integer>();
values.put("A", 235);//your custom data, the types may be different
//more data insertions....
Set<Map.Entry<String,Integer>> list = values.entrySet();//your list
//do as you may with it
}
just create a class like
class Tuples
{
int x;
int y;
}
then create List of this objects of Tuples
List<Tuples> list = new ArrayList<>();
so you can also implement other new data structures in the same way.
In project Reactor (io.projectreactor:reactor-core) there is advanced support for n-Tuples:
Tuple2<String, Integer> t = Tuples.of("string", 1)
There you can get t.getT1(), t.getT2(), ... Especially with Stream or Flux you can even map the tuple elements:
Stream<Tuple2<String, Integer>> s;
s.map(t -> t.mapT2(i -> i + 2));
What about com.sun.tools.javac.util.Pair?
First Thing on my mind when talking about key/value pairs is the Properties Class where you can save and load items to a stream/file.
You can reuse existing Pair or any such class from "God knows how many libraries already provide such classes". And If you do not want anything from Hans Brende's answer on this question then I don't see any reason for not using 2D Array or List of Object Arrays/ArrayLists used as Pairs/Tuples. The reason for not using Array, you mentioned:
I also realize that I could use a 2D array, but because of the different types I need to use, I'd have to make them arrays of OBJECT, and then I'd have to cast all the time.
Even if you use Pair class from the Accepted Answer, you'll still have to cast the key and value objects. Since you want to store objects of all the types in there. In other words List<Pair> pairs is no different from List<Pair<? extends Object>> pairs which in turn is no different from Object[][2] or List<Object[]> or List<List<Object>>. Because if you write the following code:
List<Pair> pairs = new ArrayList<>();
// logic for populating pairs into list goes here
// then after sometime you need an element
Pair p = pairs.get(whateverIndex);
Object key = p.getKey(); // We don't know type of key, right?
Object value = p.getValue(); // We don't know the exact type here as well
// All sorts of type guessing statemntes go here
GuessedTypeOfKey finallyMyKey = (GuessedTypeOfKey) key;
GuessedTypeOfValue finallyMyValue = (GuessedTypeOfValue) value;
You still have to do the type casting. So I don't find any other reason to not use 2d Object array or List of Object Arrays/ArrayLists used as Pairs/Tuples . Following is code using List and arrays
List<Object[]> simplePairs = new ArrayList<>();
// Add values to pairs
simplePairs.add(new Object[]{1,"One"});
simplePairs.add(new Object[]{"Another Key of Different Type","Value"});
simplePairs.add(new Object[]{"Another Key of Different Type",new AnotherType("Another Value Type")});
// get values
Object[] pair = simplePairs.get(whateverIndex);
Object key = pair[0];
Object value = pair[1];
What you want is a List of some kind of object. I personally do not like using generic Pair classes. They have too many disadvantages:
They are not very expressive. They provide no contextual information besides their type arguments.
They prevent you from using primitive types
You can't apply any constraints on keys or values (besides their
type, of course).
They force you to carry the verbose type parameter declaration in many places (though this is somewhat mitigated by var and <>)
I prefer using ad-hoc classes. While a few years ago, this came with annoying boilerplate, this is no longer the case.
Here is a couple of alternatives that makes the declaration of the class really easy:
Java 14 and above: use a record class
record TypedCount(String type, int count) {}
There, that's it. You get toString() and equals() for free
Before Java 14: use lombok's #Value annotation
#Value
class TypedCount {
String type;
int count;
}
That's also it. Lombok automatically makes the fields private final, constructors, getters, toString() and equals().
While this requires you to add lombok as a dependency, this is only a compile-time dependency that generates the code for you. The lombok library does not need to be in your classpath at runtime.

Categories

Resources