I'm trying to weigh the pros and cons of using an EnumMap over a HashMap. Since, I will always be looking up using a String, it seems that a HashMap with a String key would be the right choice. However, an EnumMap seems like better design because it conveys my intent to restrict keys to a specific enumeration. Thoughts?
Here is a make-believe example, showing how I will be using the Map:
enum AnimalType { CAT, DOG }
interface Animal {}
class Cat implements Animal {}
class Dog implements Animal {}
public class AnimalFactory {
private static final Map<AnimalType, Animal> enumMap
= new EnumMap<AnimalType, Animal>(AnimalType.class);
// versus
private static final Map<String, Animal> stringMap
= new HashMap<String, Animal>();
static {
enumMap.put(AnimalType.CAT, new Cat());
enumMap.put(AnimalType.DOG, new Dog());
stringMap.put("CAT", new Cat());
stringMap.put("DOG", new Dog());
}
public static Animal create(String type) {
Animal result = enumMap.get(AnimalType.valueOf(type));
Animal result2 = stringMap.get(type);
return result;
}
}
Assume that the AnimalType enum and map will ONLY be used by the AnimalFactory to create animals and nowhere else.
Which Map should I use?
If all valid keys can be enumerated, I would use that as it ensures you are always working with a valid value.
It can also avoid confusion as String can be used for lots of things and is easy to turn an "Animal" string into a string used for something else. As enum types are not interchangable with other types in general (unless you use a common interface), there is less chance of error in coding.
If the set of possible keys is finite and known in advance (as your example/question suggest), then the enum is a perfect representation of this. As other said, the use of an enum ensures that no mistake will be made in the use of the key.
Furthermore, this implementation of Map is quite optimized, as the range of the keys is known in advance (as far as I knwow, the EnumMap uses an array of length numberOfEnums internally, indexed by the enum's ordinal).
So I would also recommend EnumMap.
Two (little) things to keep in mind though :
you won't be able to add specialized cases by inheritance (you cannot extend an enum, so no Maps of ANIMALS with specialized Maps of MAMMALS on the side for example)
when adding a member to your enum, if you add it "in the middle" of the other members you change the ordinals. As this info can be used by the EnumMap, this can prove problematic if you reload an EnumMap constructed from the old version of the enum (for example using Serialization)
Key Of Map should be unmodifiable and Unique and this can be guranteed using Enum.
Also managing it would be easier and less error prone as compare to managing String.
So Go For EnumMap.
Also as we have advanced enums, we can attach many other information and operations with the keys itself.
enum AnimalType {
Dog("I am dog and I hate cats", true),
CAT("I am cat and I love to eat rats", true),
RAT("I am a mouse and I love tearing human cloths apart", false) ;
private final String description;
private final boolean isHelpFullToHuman;
private AnimalType(String description , boolean isHelpFullToHuman) {
this.description = description;
this.isHelpFullToHuman = isHelpFullToHuman;
}
public boolean isHelpFullToHuman() {
return isHelpFullToHuman;
}
public String getDescription() {
return description;
}
}
First of all, all you keys are final immutable. You should definitely use EnumMap.
This is like Hash in Ruby:
options = { :font_size => 10, :font_family => "Arial" }
:font_size is the symbol in Ruby, the final static counterpart in Java.
Related
I have a list of POJOs I need to sort somehow. I define a Comprator inside the POJO class and use it to sort the list.
Is the following way correct/best practice? Is there a better way to do it?
public class CompratorTest {
public static void main(String[] args) {
List<Person> people = List.of(
new Person("zoe", "saturday", 40),
new Person("luca", "red", 15),
new Person("boris", "vin", 54),
new Person("boris", "apple", 33),
new Person("boris", "apple", 70)
);
List<Person> sortedPeople =
people.stream()
.sorted((person, other) -> Person.COMPARATOR.compare(person, other))
.collect(Collectors.toList());
sortedPeople.forEach(System.out::println);
}
#Data
#AllArgsConstructor
static
class Person {
final static Comparator<Person> COMPARATOR =
Comparator.comparing((Person person) -> person.getName())
.thenComparing(person -> person.getSurname())
.thenComparing(person -> person.getAge());
String name;
String surname;
int age;
}
}
Output is correct, by the way.
EDIT
Adding a more classic way:
#Data
#AllArgsConstructor
static class Animal implements Comparable<Animal> {
String name;
String race;
#Override
public int compareTo(Animal other) {
if (this.name.equals(other.name)) {
return String.CASE_INSENSITIVE_ORDER.compare(this.race, other.race);
}
return String.CASE_INSENSITIVE_ORDER.compare(this.name, other.name);
}
}
Which one do you think is a better solution?
There's a substantial distinction between the use cases for Comparator and Comparable.
Implementing the Comparable interface is suitable for objects that have a natural order in your domain model. I'm not sure whether animals have a natural order, but if it is the case from the perspective of how your application model the animals, that's fine - that's the way to go. Otherwise, your class should not implement Comparable.
It's not something opinion-based, documentation clearly defines when these interfaces are intended to be used.
Comparable:
This interface imposes a total ordering on the objects of each class
that implements it. This ordering is referred to as the class's
natural ordering, and the class's compareTo method is referred to as
its natural comparison method.
Comparator:
Comparators can also be used to control the order of certain data structures (such as sorted sets or sorted maps), or to provide an ordering for collections of objects that don't have a natural ordering.
Another obvious distinction, that you can define as many flavors of comparators as you need. Which is handy when there's no one specific way to compare and sort the objects. And they must have more meaningful names than comparator.
Personally, I don't see a huge harm in defining a couple of comparators as public static final fields, as in your example. If you have a single class that manages the instances of this type - extract the comparators into that class, otherwise if these objects are ubiquitous and used in many places you can leave them right inside the POJO (that an opinion based part).
This is not opinion based: TL;DR implement Comparable:
semantically, this is what Interfaces are designed for: they express a contract enforced by an object, a behavior of the object: if the objects are serializable, then they should implement Serializable, if they are comparable, then they should implement Comparable, etc...
inheritance will work as expected and be more readable: if you define a Dog that extends Animal, you can implement comparison for Dog using the super implementation (i.e. a Dog is compared like any other Animal) or overriding the implementation to implement a behavior specific to Dog. The user of your Dog class simply calls instance.compareTo(...) without having to worry about what final static comparator she/he should call
users of your Animal API know they have to implement Comparable when adding their own animal to the inheritance tree
So I've learned a bunch recently so I'm going back and sort of refactoring the homeworks from a previous course that I took to implement them using good practices. One homework had us implement a Planner object that contained an array of Course objects. I'm trying to create some Course constants so that I can access some popular Courses without having to create brand new Objects every time and so I can easily access them without going through the Course building process. I don't have much experience with enums and I can't seem to find anything on how I can actually use an Enum to store constants that are Objects. I originally wanted to make them constants in the Course class but Effective Java insists enumns should be used in such a situation. Does my implementation make sense at all? How should I go about making this enum that contains Course constants so I can actually retrieve them? I use the Builder method for creating a Course.
public enum Courses {
CSE_114, CSE_214, CSE_219, CSE_215;
private final static Course CSE_114_COURSE = new Course
.Builder("Computer Science 1", "Paul Fodor", 114)
.section((byte)1).department("CSE").build();
private static final Course CSE_214_COURSE = new Course
.Builder("Data Structures", "Ahmad Esmaili", 214)
.section((byte)1).department("CSE").build();
private static final Course CSE_219_COURSE = new Course
.Builder("Software Development", "Richard McKenna", 219)
.section((byte)1).department("CSE").build();
private static final Course CSE_215_COURSE = new Course
.Builder("Foundations of CS", "Paul Fodor", 215)
.section((byte)1).department("CSE").build();
public static Course get(Courses c) {
switch(c) {
case CSE_114: return CSE_114_COURSE;
case CSE_214: return CSE_214_COURSE;
case CSE_219: return CSE_219_COURSE;
case CSE_215: return CSE_215_COURSE;
default: throw new IllegalArgumentException("Course does not exist.");
}
}
}
You can actually treat an enum like an object:
public enum Course {
CSE_114("Computer Science 1", "Paul Fodor");
public final String room;
public final String lecturer;
private Course(room, lecturer) {
this.room = room;
this.lecturer = lecturer;
}
}
Because it is an enum, all values must be known at compile time. This is enforced by the Java language, which requires that the enum constructor is private.
While this would work for your situation, I don't recommend it - in fact, I don't recommend using an enum at all. An enum represents a fixed, known set of values. If you want to create more courses at runtime, then the enum is incomplete, and that contradicts the definition of an enum.
Instead, I suggest you use a CourseManager. Create one class, which holds the collection of all known courses. Then, when you need a course, you request it by name.
Course cs114 = courses.get("CS 114");
You could also take it one step further, by instantiating the CourseManager from a file, which contains a list of courses in a basic format like JSON.
That's not a bad implementation but I think a have a bit better solution: add an abstract method to your enum definition.
public enum Courses {
CSE_114 {
public Course getCourse() {
return CSE_114_COURSE;
}
}
...
private final static Course CSE_114_COURSE = new Course
.Builder("Computer Science 1", "Paul Fodor", 114)
.section((byte)1).department("CSE").build();
...
public abstract Course getCourse();
}
That way you can access to any course object vía Courses.CSE_114.getCourse() and you are also forcing that every enum has a course (imagine that you add an enum constant but forgot to add it in the get method. The way I proposed makes that scenario non sense)
You are mixing enum constants with static constants. Get these courses and selection of courses out of the enum class. Or you can use something like Andrew Williamson showed. Just don't mix the static constants with the enums. Static constants don't belong to the enum class. Enum class has a fixed set of values and that is where its role ends. All the other logic using these enums should be done somewhere else
Without getting bogged down with specifics, my code represents a library whereby each book is made up of a Set of pages containing a Set of Words.
I have created my own Set implementations:
class PageSet<E> extends HashSet<E>(){
public boolean set(int index, E e){....}
....
}
and
class WordSet<E> extends HashSet<E>(){
public boolean set(int index, E e){....}
....
}
I've got stuck when I try to create a Book in my main class:
Set<Set<Word>> dictionary = new PageSet<WordSet<Word>>();
Which results in a type conversion mismatch. However it will quite happily accept
Set<Set<Word>> dictionary = new PageSet<Set<Word>>();
Could someone please shed some light as to what I'm doing wrong when using a generic setup like this?
Basically, a PageSet<WordSet<Word>> is not a Set<Set<Word>>, because X<Subclass> is not a X<Superclass>.
If you had said
Set<WordSet<Word>> dictionary = new PageSet<WordSet<Word>>();
then that would have worked also.
It's either
Set<Set<Word>> dictionary = new PageSet<Set<Word>>();
or
Set<WordSet<Word>> dictionary = new PageSet<WordSet<Word>>();
Since although WordSet is a subclass of Set, a Set<WordSet> is not a subclass of Set<Set>.
In other words, generics are not covariant, which is different from things like arrays.
In any case, you should not extend collections unless you are trying to create new collection types. Since you cannot restrict the visibilities of superclass methods in a subclass, people will be able to write
WordSet<Word> words = ...;
words.clear();
You probably do not want to give clients that power. Instead, use aggregation instead of inheritance.
class Word {
private String text;
private PartOfSpeech part;
// Constructors, getters, setters, equals, hashCode are elided.
}
class Page {
private int pageNumber;
private Set<Word> contents = new HashSet<>();
public class Book {
private String title;
private List<Page> pages = new ArrayList<>();
}
Pages in a book are ordered linearly, which is why I used lists. I'm not sure why you used sets. But in any case, by encapsulating the collections inside the classes, you can provide client code exactly the interface you want them to use. The visibilities were chosen deliberately; this looks like a cluster of related classes, but you might want to change them.
I am writing a program to simulate cities from a game called Civilization 4. In order to do this I have several Enums to represent types of terrain, resources, improvements etc for each plot owned by said city.
The problem is I want to program to be compatible with Fan made mods which may add things to the Game that need to be accepted into my independant utility. So I thought of creating a Enum style class to hold the new types defined by the loaded mods (as Enums cannot be changed at runtime) which is created during runtime when the user enters in a mod to be loaded (which is a txt file that is parsed to read the new additions)
So is there a way to simulate Enums that are created and added to at runtime? I take it static member variables cannot be used as they are done before runtime...
You can make a enum implement an interface.
This way you can have your defined values in the enum, but new values can be any class which implements the interface.
An alternative is that you generate or load the enum at runtime using a byte code generator or the Compiler API. I wrote a library to make it easier to take a String and compile&load it.
http://vanillajava.blogspot.co.uk/2010_11_01_archive.html
Well, enums in Java are simply classes where the language guarantees that the set of known objects is known and limited at compile-time. If you want to add new enum literals at runtime, you end up with regular classes.
The beauty of enums is that you can write human readable names in code that are compiled as numbers behind the scenes, because computers like numbers better. Take for example this enum:
enum Season { WINTER, SPRING, SUMMER, AUTUMN }
Behind the scenes WINTER might be 0 (zero), SPRING is 1 etc.
To replicate this behaviour in runtime code you could create a list of strings, like this:
List<String> seasons;
seasons = new ArrayList<String>();
seasons.add("Winter");
seasons.add("Spring");
...
That way you can reference the items as numbers, such as seasons[1] would equal "Spring".
This answer is just one of many ways to approach this question.
By default, enum types have only a set number of values. The values in an enum type are actually declared as static final, and there's no way to add more on runtime.
That being said, there are other patterns you can use to implement what you want. Let's take a look at using an interface and a registration system. We'll start with the Terrain interface:
public interface Terrain {
int getId();
String getName();
int getSightBonus();
}
Now an enum, DefaultTerrain:
public enum DefaultTerrain implements Terrain {
PLAINS(0, "Plains", 1),
HILLS(1, "Hills", -1),
MOUNTAINS(2, "Mountains", -2);
private int id;
private String name;
private int sightBonus;
private DefaultTerrain(int id, String name, int sightBonus) {
this.id = id;
this.name = name;
this.sightBonus = sightBonus;
}
public int getId() {return id;}
public String getName() {return name;}
public int getSightBonus() {return sightBonus;}
}
And a registration class, which can be either a static utility class or a singleton.
public class TerrainManager {
private static Map<Integer, Terrain> terrainsById = new HashMap<>();
static {
for (DefaultTerrain terrain : DefaultTerrain.values())
register(terrain);
}
public static void register(Terrain terrain) {
Integer id = terrain.getId();
if (terrainsById.contains(terrain.getId()))
throw new IllegalArgumentException("Terrain with id already exists: " + id);
terrainsById.put(id, terrain);
}
public static Terrain getTerrainById(int id) {
return terrainsById.get(id);
}
public static Set<Terrain> getAllTerrains() {
return new HashSet<Terrain>(terrainsById.values());
}
}
This last class is where the magic happens. Presumably the modders will have some kind of identifier in the game's world definition to say "use this tile," right? In this case, I've called it an integer, id, but really it could be any type, just modify the Map accordingly. In the map-loading code, just use the ID in the world definition to look up the Terrain. When a modder adds a new Terrain, they just need to implement Terrain and register it with TerrainManager.
The static initializer makes sure that your DefaultTerrain objects are added before anything else is added. If you use a singleton, this could be put into the class constructor instead.
Use this pattern for your different enum types that you want users to add to. You could also use it for pretty much any other type as well besides enum.
You will need CGLIB, http://cglib.sourceforge.net/
This question was asked to me in MS interview. I wanna know the exact design issue in this piece of code. Code was already given, needed to find the design issue.
I have class MyHashMap which extends java HashMap class. In MyHashMap class I have to keep some information of employees. Key in this map will be firstName+lastName+Address .
public MyHashMap extends HashMap<Object, Object> {
//some member variables
//
public void put(String firstName, String lastName, String Address, Object obj) {
String key = firstName + lastName+ Address;
put(key, obj);
}
public Object get(String firstName, String lastName, String Address) {
String key = firstName + lastName+ Address;
return get(key);
}
public void remove(Strig key) {
put(key, "");
}
//some more methods
}
What is wrong with this design? Should I subclass HashMap or should I declare
HashMap as member variable of this class? Or should I have implemented hashCode/equals methods?
There are quite a few problems, but the biggest problem I can see it that you're using a concatenated String as a key. The following two calls are different, but equivalent:
final MyHashMap map = new MyHashMap();
map.put("foo", "", "baz", new Object());
map.put("", "foo", "baz", new Object()); // Overwrites the previous call
There's also an issue that you're declaring that the key type as an Object, but always using String and are therefore not taking advantage of the type safety that comes with generics. For example, if you wanted to loop through the keySet of your Map, you'd have to cast each Set entry to a String, but you couldn't be sure that someone didn't abuse you Map by using an Integer key, for example.
Personally, I would favour composition over inheritance unless you have a good reason not to. In your case, MyHashMap is overloading the standard Map methods of put, get and remove, but not overriding any of them. You should inherit from a class in order to change its behaviour, but your implementation does not do this, so composition is a clear choice.
To act as an example, overloading rather than overriding means that if you make the following declaration:
Map<Object, Object> map = new MyHashMap();
none of your declared methods will be available. As recommended by some of the other answers, it would be far better to use an object composed of firstName, lastName and address to act as your map key, but you must remember to implement equals and hashCode, otherwise your values will not be retrievable from the HashMap.
What's wrong with that design is primarily that is claims to be a HashMap<Oject, Object>, but isn't really. The overloaded methods "replace" the Map methods, but those are still accessible - now you're supposed to use the class in a way that is incompatible with the Map interface and ignore the (still technically possible) compatible way to use it.
The best way to do this would be to make an EmployeeData class with name and address fields and hashCode() and equals() methods based on those (or, better yet, a unique ID field). Then you don't need a non-standard Map subclass - you can simply use a HashMap<EmployeeData, Object>. Actually, the value type should be more specific than Object as well.
I would go for declaring HashMap as member variable of your class.
I don't remember if a great explanation is given in Clean Code or Effective Java, but basically, it's simplier to do, requires less work if the API changes.
generally, extending such a class means you want to change its behavior.
The map uses an internal key based on first name, last name and address. So the remove method should (1) be implemented as remove(String firstName, String lastName, String Address) and (2) it should not change the behaviour of the original remove method (which really deleted the entry) by just changing the value. A better implementation of remove would be:
public void remove(String firstName, String lastName, String address) {
String key = firstName + lastName + address;
return remove(key);
}
My first take would be that:
public MyHashMap extends HashMap<Oject, Object>
is wrong. No type safety.
If a Map is required then at least make it
public MyHashMap extends HashMap<NameAddress, Employee>
and adjust the put and get the same. A NameAddress use firstname, lastname and address
in the equals and hashCode.
I personally feel that a Set interface would match better, with possibly a HashMap as a member. Then you could define the interface like it should be:
public EmployeeSet implements Set<Employee> {
private static class NameAddress {
public boolean equals();
public int hashCode();
}
private HashMap employees = new HashMap<NameAddress, Employee>();
public add(Employee emp) {
NameAddress nad = new NameAddress(emp);
empoyees.add(nad, emp);
}
public remove(Employee emp) {
}
}
and extract name and address info within the implementation to create the key.
EDIT David pointed out that NameAddress doesn't need to be Comparable and I removed that part of the interface. Added hashCode for correctness.
Two other "heinous crimes" against good practice are that the remove method:
overloads Map.remove(<K>) rather than overriding it (so you have a leaky abstraction), and
implements a behavior that is incompatible with the behavior of the method it overrides!
Setting the value associated with a key to an empty string is NOT THE SAME as removing the entry for the key. (Even setting it to null is subtly different ...)
This is not a violation of the Liskov substitutability principle, but it could be really confusing for someone trying to use the type. (Depending on whether you called the method via the Map type or the MyHashMap type you could end up using different methods with different semantics ....)
I think it depends a lot on the context and what they exactly asked. For example, is a highly concurrent context, you should have used Hashtable instead. Also, you should specify the types (String, Object) instead of (Object,Object) to get compiler support on the keys.
I think a better design would have been to implement the Map interface and keep the HashMap/HashTable as an internal parameter of the object.