How to avoid having different methods looping the same list? - java

I have a class that is mapped from a xml. To make it simple, let's imagine this class is something like:
class Employee implements EmployeeIF {
Map<AttributeIF,Object> attribute = new HashMap<>();
#Override
public Map<AttributeIF,Object> getAttributes() { return attribute; }
}
This is something I cannot change.
Now, the existing code is full of methods like:
public int getSalary(EmployeeIF employee) {
for(Entry<AttributeIF,Object> entry : employee.getAttributes()) {
if(entry.getKey().getName().equals("salary")) return (Integer)entry.getValue();
}
return 0;
}
public int getAddress(EmployeeIF employee) {
for(Entry<AttributeIF,Object> entry : employee.getAttributes()) {
if(entry.getKey().getName().equals("address")) return (String)entry.getValue();
}
return "";
}
... and so on. Surely you got the idea.
I need to include a new method to return a new attribute from the employee, but as I feel this is horrible to mantain, I refuse to just add a new method there.
I am thinking on using the action pattern to somehow avoiding at least repeating againg and again the for loop but I have to say that I cannot find a smart solution for this.
What would be your choices?
Thanks,
Dani.
P.D Yes I tried something like
private Object getAttribute(EmployeeIF employee, String attribute)

Here is a tiny example how you could get, based on a object as key that you donĀ“t have, the value.
public class TestObject {
public String val;
public TestObject(String val) {
this.val = val;
}
public static TestObject createDummy(String val) {
return new TestObject(val);
}
#Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof TestObject)) return false;
return ((TestObject)obj).val.equals(this.val);
}
#Override
public int hashCode() {
System.out.println("THIS ONE IS IMPORTANT");
return val.hashCode();
}
}
public class TestMap {
public Map<TestObject, String> map = new HashMap<>();
public String get(String keyVal) {
return map.get(TestObject.createDummy(keyVal));
}
public static void main(String[] args) {
TestMap map = new TestMap();
TestObject o1 = new TestObject("A");
map.map.put(o1,"B");
TestObject o2 = new TestObject("B");
map.map.put(o2,"C");
TestObject o3 = new TestObject("C");
map.map.put(o3,"D");
System.out.println(map.get("B"));
}
}
The Key to it, is to override equals and hashCode in your AttributeIF class. So in case you are passing a dummy object of they AttributeIF you do want to have your map needs to identify this dummy object to be equal with the instance of an theoretcly "equal" key object instance inside your Map.

Related

OO: Does container contain bike or chair?

A container may contain bikes and chairs, both belonging to a person. I would like to check, if the container contains either bikes or chairs of said person. Is this possible without using instanceof?
public class Container {
public Map<Person, List<Item>> items = new HashMap<>();
public void add(Person p, Item item) {
items.get(p).add(item);
}
public boolean containsChair(Person owner) {
for(Item i : items.get(owner)) {
if(i instanceof Chair) {
return true;
}
}
return false;
}
public boolean containsBike(Person owner) {
for(Item i : items.get(owner)) {
if(i instanceof Bike) {
return true;
}
}
return false;
}
}
For the purpose of illustration, Item, Bike, Chair, Person are all simplest class stubs:
public class Person { public String name; }
public abstract class Item {}
public class Bike extends Item { public Wheel[] wheels;}
public class Chair extends Item { public Leg[] legs;}
public class Wheel {}
public class Leg {}
In the runner, a Person should be able to add Chairs and Bikes to its container:
import java.util.ArrayList;
public class Runner {
public static void main(String[] args) {
Container c = new Container();
Person p = new Person();
// Prevent null pointer exception
c.items.put(p, new ArrayList<>());
c.add(p, new Chair());
// True
System.out.println(c.containsChair(p));
}
}
You could add to class Item an abstract method ItemType getType(). ItemType would be an enum enumerating all possible item types.
public abstract class Item {
public abstract ItemType getType();
}
public enum ItemType {
BIKE, CHAIR;
}
Implementation of Chair:
public static class Chair extends Item {
public Leg[] legs;
#Override
public ItemType getType() {
return ItemType.CHAIR;
}
}
Then you could define a contains method to search for a the given Person if it has an item with a certain ItemType:
public boolean contains(Person owner, ItemType itemType) {
return items.get(owner).stream().anyMatch(item ->itemType.equals(item.getType()));
}
Or null-safe regarding the owners items list:
public boolean contains(Person owner, ItemType itemType) {
return Optional.ofNullable(items.get(owner))
.map(i -> i.stream().anyMatch(item -> itemType.equals(item.getType())))
.orElse(false);
}
Usage:
public static void main(String[] args) {
Container c = new Container();
Person p = new Person();
// Prevent null pointer exception
c.items.put(p, new ArrayList<>());
c.add(p, new Chair());
// True
System.out.println(c.contains(p, ItemType.CHAIR));
}
EDIT
Following this approach there is no need for instanceof checks. The usage of instanceof can be a hint indicating that the design has some flaws.
You can store Bike and Chair in two different datastructure.
public final class Container {
private final Map<Person, List<Chair>> chairs = new HashMap<>();
private final Map<Person, List<Bike>> bikes = new HashMap<>();
public void add(Person p, Chair chair) {
chairs.putIfAbsent(p, new ArrayList<Chair>());
chairs.get(p).add(chair);
}
public void add(Person p, Bike bike) {
bikes.putIfAbsent(p, new ArrayList<Bike>());
bikes.get(p).add(bike);
}
public boolean containsChair(Person owner) {
return chairs.getOrDefault(owner, Collections.emptyList()).size() > 0;
}
public boolean containsBike(Person owner) {
return bikes.getOrDefault(owner, Collections.emptyList()).size() > 0;
}
}
Note that I also made your instance fields private to hide the fact that data is stored in a Map and avoid the runner code to have the responsibility to instanciate an ArrayList if not existant. Both the class and its fields are also final to achieve a better immutability. Both encapsulation and immutability are considered good practices when doing OOP.
Usage
public static void main(String[] args) {
Container c = new Container();
Person p = new Person();
c.add(p, new Chair());
System.out.println(c.containsChair(p)); //true
System.out.println(c.containsBike(p)); //false
}
What I ended up doing was to add two methods to Item:
public boolean containsBike() {return false;}
public boolean containsChair() {return false;}
While this certainly could be optimized, the check is now done by calling the method of the object:
public boolean containsBike(Person p) {
boolean hasBike = false;
// Prevent NullPointerException
if(containsSomethingOf(p)) {
for(Item i : items.get(p)) {
if(i != null) {
if (i.containsBike()) {
hasBike = true;
}
}
}
}
return hasTrousers;
}
I think this is what is called polymorphism.

Not sure how I am supposed to keep this Arraylist from being editable

I have this class, Party, in which I have an arraylist RSVP and an arraylist invited. The goal is that One should be able to add a name to these arraylists using my addInvited() and get it using getInvited(). I know the problem is in one of these two methods, as every other method has passed its test. I need to make it so that someone can add a Person object using addInvited(), but that Person CANNOT change his name. I can't seem to figure out if I'm just not making a deep enough copy, or what...
package lab04partB;
import java.util.ArrayList;
public class Party {
private ArrayList<Person> invited;
private ArrayList<Person> RSVP;
public Party() {
invited = new ArrayList<Person>();
RSVP = new ArrayList<Person>();
}
public void addInvited(Person person) {
if (!invited.contains(person)) {
Person JohnDoe = new Person(person.getName());
invited.add(JohnDoe);
}
}
public ArrayList<Person> getInvited() {
ArrayList<Person> tempList = new ArrayList<Person>(invited);
return tempList;
}
public void addRSVP(Person person) {
if ((!RSVP.contains(person)) && (invited.contains(person))) {
Person JaneDoe = new Person(person.getName());
RSVP.add(JaneDoe);
}
}
public ArrayList<Person> getRSVP() {
ArrayList<Person> tempList = new ArrayList<Person>(RSVP);
return tempList;
}
}
Here is the test it is running against, if it helps!
#Test
public void testGetInvitedModifyNamesReturned() {
Party party = new Party();
Person a = new Person( new String( KANY_GARCIA ));
Person b = new Person( new String( LAURA_PAUSINI ));
party.addInvited( a );
party.addInvited( b );
ArrayList<Person> list = party.getInvited();
assertEquals( 2, list.size() );
for (Person p : list) {
p.setName( new String( MIGUEL_RIOS ));
}
list = party.getInvited();
assertEquals( "Incorrect result", 2, list.size() );
assertTrue ( "Incorrect result", list.contains( a ));
assertTrue ( "Incorrect result", list.contains( b ));
}
One way would be to make the name field in the Person class final but then no one can change the name of a person anywhere. Guess that is not what you want.
Alternatively you can create an inner class in Party that subclasses Person and disallows changing the name. Then when you add a person to a party you first convert the input argument to an immutable person.
class Party {
private static final class ImmutablePerson extends Person {
public ImmutablePerson(String name) {
super(name);
}
#Override
void setName(String s) {
throw new RuntimeException("Cannot change name");
// or just do nothing here
}
}
public void addInvited(Person person) {
ImmutablePerson immutable = new ImmutablePerson(person.getName());
if (!invited.contains(immutable)) {
invited.add(immutable);
}
}
}
Make Person immutable. It's actually best practice too, because people don't change data, and (as here) you can safely publish them.
There's a few ways you might be able to do this...
You could use interfaces to maintain the contractual expectations of the API, so you could setup non-mutable version of Person (with getters) and a mutable version (with setters), this would mean that your Party class could return the non-mutable types, preventing people from directly modifying the values.
This could lead to using the non-mutable instance as a wrapper for the mutable version, further preventing people from casting the results to get around this.
Something like...
public interface Person extends Comparable<Person> {
public String getName();
}
public class DefaultPerson implements Person {
private String name;
public DefaultPerson(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (!(obj instanceof Person)) {
return false;
}
final Person other = (Person) obj;
if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
return false;
}
return true;
}
#Override
public int hashCode() {
int hash = 3;
hash = 37 * hash + Objects.hashCode(this.name);
return hash;
}
#Override
public int compareTo(Person arg0) {
return arg0.getName().compareTo(name) * -1;
}
public Character[] toCharacterArray(String s) {
if (s == null) {
return null;
}
int len = s.length();
Character[] array = new Character[len];
for (int i = 0; i < len; i++) {
array[i] = new Character(s.charAt(i));
}
return array;
}
#Override
public String toString() {
return getName();
}
}
Then your Party might look something like...
public static class Party {
private ArrayList<Person> invited;
private ArrayList<Person> RSVP;
public Party() {
invited = new ArrayList<Person>();
RSVP = new ArrayList<Person>();
}
public void addInvited(Person person) {
if (!invited.contains(person)) {
invited.add(person);
}
}
public List<Person> getInvited() {
return Collections.unmodifiableList(invited);
}
public void addRSVP(Person person) {
if ((!RSVP.contains(person)) && (invited.contains(person))) {
RSVP.add(person);
}
}
public List<Person> getRSVP() {
return Collections.unmodifiableList(RSVP);
}
}
The class know only deals with Person types, which are not mutable (okay, you could create instances of DefaultPerson and add them to your lists, which would prevent the caller from modifying the names, but that's up to you)
The class also makes use Collections.unmodifiableList which prevents the List from been modified by the caller! Bonus :)
It would then mean, doing something like...
List<Person> list = party.getInvited();
for (Person p : list) {
p.setName(new String("Whelma"));
}
would be impossible, because setName is not a method of Person!
But...
that might be beyond the scope of your assignment, instead, when you return the list of invitees, you could create new instances of the values then, for example...
public void addInvited(Person person) {
if (!invited.contains(person)) {
invited.add(person);
}
}
public ArrayList<Person> getInvited() {
ArrayList<Person> tempList = new ArrayList<>(invited.size());
for (Person p : invited) {
tempList.add(new Person(p.getName()));
}
return tempList;
}
This is less the optimal, but it would allow your code to pass the tests you have.
I should also point out, equals has a contractual relationship with hashcode, from the JavaDocs:
Note that it is generally necessary to override the hashCode method whenever this method is overridden, so as to maintain the general contract for the hashCode method, which states that equal objects must have equal hash codes.
Basically, what this means is, if you override equals you must also override hashcode (and visa-versa)

Unique classes in generic list

I have a generic class with a generic list in it. I want to ensure that the generic list only contains unique classes.
What I have done so far is to compare the class names with reflection (getClass()). But I think that's not a clean solution. Are there any better practices to check?
public class MyGenericClass<T extends MyGenericClass.MyInterface> {
private List<T> members = new ArrayList<>(0);
public void add(T t) {
final boolean[] classInMembers = {false};
members.forEach(member -> {
if (member.getClass().getName().equals(t.getClass().getName())) {
classInMembers[0] = true;
}
});
if (!classInMembers[0]) {
members.add(t);
}
}
public interface MyInterface {
void doSomething(String text);
}
}
public class Main {
public static void main(String[] args) {
MyGenericClass<MyGenericClass.MyInterface> myGenericClass = new MyGenericClass<>();
myGenericClass.add(new Performer1());
myGenericClass.add(new Performer2());
myGenericClass.add(new Performer3());
myGenericClass.add(new Performer3()); // should not be inserted!
}
private static class Performer1 implements MyGenericClass.MyInterface {
#Override
public void doSomething(String text) {
text = "Hi, I am performer 1!";
}
}
private static class Performer2 implements MyGenericClass.MyInterface {
#Override
public void doSomething(String text) {
text = "Hi, I am performer 2!";
}
}
private static class Performer3 implements MyGenericClass.MyInterface {
#Override
public void doSomething(String text) {
text = "Hi, I am performer 3!";
}
}
}
You could subclass a java.util.Set interface implementation. It will likely be easiest to subclass java.util.AbstractSet.
By default 'Set' will compare objects by their .equals() method - In your case, this is not sufficient. You will need to override the contains method to ensure that only instances of a unique class are added.
In your overrideen contains, it's probably the same / easier to compare class instances rather than their stringified package name
I.e. use a.getClass() == b.getClass(), rather than a.getClass().getName()
Don't use a List, use a java.util.Set instead.
A collection that contains no duplicate elements. More formally, sets contain no pair of elements e1 and e2 such that e1.equals(e2), and at most one null element.
If the iteration order is important or if you want to use a custom Comparator, the TreeSet implementation can be used:
A NavigableSet implementation based on a TreeMap. The elements are ordered using their natural ordering, or by a Comparator provided at set creation time, depending on which constructor is used.
Example of a Set using a Comparator:
class MyComparator implements Comparator<Object> {
#Override
public int compare(Object e1, Object e2) {
if (e1.getClass() == e2.getClass())
return 0;
//if you wish to have some extra sort order
return e1.getClass().getName().compareTo(e2.getClass().getName());
}
}
. . .
Set mySet = new TreeSet<Object>(new MyComparator());
mySet.add(new Object());
mySet.add(new Object());//same class already in set
mySet.add("wtf");
//mySet.size() is now 2 - the second "new Object()" was not inserted due to the comparator check
Why so complicated?
public class Main {
public static void main(String[] args) {
final Class<?> helloClass = "Hello".getClass();
final Class<?> worldClass = "World".getClass();
final Class<?> intClass = Integer.class;
System.out.println(helloClass.equals(worldClass)); // -> true
System.out.println(helloClass.equals(intClass)); // -> false
}
}
You could maintain a roster of members in a Set.
public static class MyGenericClass<T extends MyGenericClass.MyInterface> {
private List<T> members = new ArrayList<>(0);
// Add this.
private Set<Class<?>> roster = new HashSet<>();
public void add(T t) {
if (!roster.contains(t.getClass())) {
members.add(t);
roster.add(t.getClass());
}
}
private void soundOff() {
for (T t : members) {
t.doSomething();
}
}
public interface MyInterface {
void doSomething();
}
}
private static class Performer implements MyGenericClass.MyInterface {
final int n;
public Performer(int n) {
this.n = n;
}
#Override
public void doSomething() {
System.out.println("Hi, I am a " + this.getClass().getSimpleName() + "(" + n + ")");
}
}
private static class Performer1 extends Performer {
public Performer1(int n) {
super(n);
}
}
private static class Performer2 extends Performer {
public Performer2(int n) {
super(n);
}
}
private static class Performer3 extends Performer {
public Performer3(int n) {
super(n);
}
}
public void test() {
MyGenericClass<MyGenericClass.MyInterface> myGenericClass = new MyGenericClass<>();
myGenericClass.add(new Performer1(1));
myGenericClass.add(new Performer2(2));
myGenericClass.add(new Performer3(3));
myGenericClass.add(new Performer3(4)); // should not be inserted!
myGenericClass.soundOff();
}
You could implement a Wrapper which provides the necessary comparison and add the wrapped instance to the set. This way you don't have to override equals and hashcode in your concrete Performer classes and you don't have to subclass a concrete Set implementation (which you are coupled to. When you subclass a HashSet, you have to use that concrete class. But what if you want to use a LinkedHashSet at some point? You have to override LinkedHashSet as well) , which may be fragile since you have to make sure that the overridden method is consistent with the rest of the class.
class MyGenericClass<T extends MyInterface> {
private Set<ClassCompareWrapper<T>> members = new HashSet<>();
public void add(T t) {
members.add(new ClassCompareWrapper<T>(t));
}
}
class ClassCompareWrapper<T> {
T t;
public ClassCompareWrapper(T t) {
this.t = t;
}
#Override
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof ClassCompareWrapper))
return false;
ClassCompareWrapper<?> that = (ClassCompareWrapper<?>) o;
return Objects.equals(t.getClass(), that.t.getClass());
}
#Override
public int hashCode() {
return Objects.hash(t.getClass());
}
#Override
public String toString() {
return "Wrapper{" +
"t=" + t +
'}';
}
}
Here are a few other ideas.
Using streams:
public void add(T t) {
if (!members.stream().anyMatch(m -> m.getClass() == t.getClass())) {
members.add(t);
}
}
Using AbstractSet and HashMap:
class ClassSet<E> extends AbstractSet<E> {
private final Map<Class<?>, E> map = new HashMap<>();
#Override
public boolean add(E e) {
// this can be
// return map.putIfAbsent(e.getClass(), e) != null;
// in Java 8
Class<?> clazz = e.getClass();
if (map.containsKey(clazz)) {
return false;
} else {
map.put(clazz, e);
return true;
}
}
#Override
public boolean remove(Object o) {
return map.remove(o.getClass()) != null;
}
#Override
public boolean contains(Object o) {
return map.containsKey(o.getClass());
}
#Override
public int size() {
return map.size();
}
#Override
public Iterator<E> iterator() {
return map.values().iterator();
}
}
A HashMap could also be used without wrapping it in a Set. The Set interface is defined around equals and hashCode, so any implementation which deviates from this is technically non-contractual. Additionally, you might want to use LinkedHashMap if the values are iterated often.

Why doesn't ConcurrentHashMap return the value even though key.equals(k)?

I have a simple object I would like to use as a key in my HashMap. MyObject is defined such that:
MyObject obj1 = new MyObject("some string");
MyObject obj2 = new MyObject("some string");
assertTrue(obj1.equals(obj2));
assertTrue(obj1.hashCode()==obj2.hashCode());
According to the doc for ConcurrentHashMap.get,
if this map contains a mapping from a key k to a value v such that key.equals(k),
then this method returns v; otherwise it returns null
However, if I try to use MyObject as the map key, retrieval fails:
ConcurrentHashMap<MyObject,String> map = new ConcurrentHashMap<MyObject,String>();
map.put(obj1,"test value");
String foundValue = map.get(obj2);
assertNotNull(foundValue); //fails
What am I missing?
EDIT: as requested, here is a full running example:
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
public class MyObject {
String name = null;
public MyObject(String name) {
this.name = name;
}
public String getName() {
return name;
}
public boolean equals(MyObject otherObject) {
boolean result = false;
if (otherObject!=null) {
if (getClass().equals(otherObject.getClass())) {
if (name==null) {
if (otherObject.getName()==null)
result = true;
} else if (name.equals(otherObject.getName()))
result = true;
}
}
return result;
}
public int hashCode() {
return Objects.hash(name);
}
public static void main(String[] args) {
MyObject obj1 = new MyObject("some string");
MyObject obj2 = new MyObject("some string");
if (obj1.equals(obj2) && obj1.hashCode()==obj2.hashCode()) {
ConcurrentHashMap<MyObject,String> map = new ConcurrentHashMap<MyObject,String>();
map.put(obj1,"test value");
String foundValue = map.get(obj2);
if (foundValue==null)
System.out.println("get returned null");
else
System.out.println("get returned my string");
} else {
System.out.println("something's not equal");
}
}
}
You have not overriden equals, you have instead created a new equals method with a different signature.
If you change
public boolean equals(MyObject otherObject) {
to
public boolean equals(Object otherObject) {
(and add appropriate casts), your code will work as expected.
You could have easily avoided that mistake by using the #Override annotation, which would have lead to a compilation error.

Set ordered by "add() count"

I'm trying to implement a Set which is ordered by the count of additions like this:
public class App {
public static void main(String args[]) {
FrequencyOrderedTreeSet<String> set = new FrequencyOrderedTreeSet<String>();
set.add("bar");
set.add("foo");
set.add("foo");
Iterator<String> i = set.iterator();
while (i.hasNext()) {
System.out.print(i.next());
}
// prints "foobar"
}
}
I've created a protected class FrequencyOrderedTreeSet.Element which implements Comparable and has a T entry and an int frequency property and extended TreeSet<FrequencyOrderedTreeSet.Element> with FrequencyOrderedTreeSet<T> and overrode the compareTo and equals methods on the Element.
One problem is that I can't override the add() method because of type erasure problems and also I can't call instanceof Element in the equals method, because in case object given to it is an Element, I have to compare their entries, but if it's not, I have to compare the object itself to this.entry.
In the add method I create a new element, find the element with the same entry in the set, set the frequency on the new element to "old+1", remove the old one and add the new one. I'm not even sure this is the best way to do this or if it would work even because the other problems I described.
The question is: what's the best way to implement such data structure? In case I'm somehow on the right track - how can I circumvent the problems I've mentioned above?
Here's a basic implementation. It's not the most optimal and will take some more work if you want to implement the full Set interface.
public class FrequencySet<T> implements Iterable<T>
{
private TreeSet<T> set;
private HashMap<T, Integer> elements = new HashMap<T, Integer>();
public FrequencySet()
{
set = new TreeSet<T>(new Comparator<T>()
{
public int compare(T o1, T o2)
{
return elements.get(o2)-elements.get(o1);
}
});
}
public void add(T t)
{
Integer i = elements.get(t);
elements.put(t, i == null ? 1 : i+1);
set.remove(t);
set.add(t);
}
public Iterator<T> iterator() {return set.iterator();}
public static void main(String [] args)
{
FrequencySet<String> fset = new FrequencySet<String>();
fset.add("foo");
fset.add("bar");
fset.add("foo");
for (String s : fset)
System.out.print(s);
System.out.println();
fset.add("bar");
fset.add("bar");
for (String s : fset)
System.out.print(s);
}
}
The key is in the add method. We change the counter for the given object (which changes the relation order), remove it from the backing set and put it back in.
This works the other way (count is increased when you use GET)
#SuppressWarnings("rawtypes")
final class Cache implements Comparable {
private String key;
private String value;
private int counter;
public String getValue() {
counter++;
return value;
}
private void setValue(String value) { this.value = value; }
public String getKey() { return key; }
private void setKey(String key) { this.key = key; }
public int getCounter() { return counter; }
public void setCounter(int counter) { this.counter = counter; }
public Cache(String key, String value) {
this.setKey(key);
this.setValue(value);
setCounter(0);
}
#Override
public int compareTo(Object arg0) {
if(!(arg0 instanceof Cache)) {
throw new ClassCastException();
}
return this.getCounter() - ((Cache) arg0).getCounter();
}
}

Categories