I have this problem by this moment, I got a class with two methods with the same erasure:
public class VO implements Map<String,String> , Serializable{
public synchronized String put (Object key, Object value){
if (key == null)
return null;
if (value == null)
return remove(key);
String stringKey = key.toString();
String stringValue = value.toString();
if (value instanceof Boolean)
stringValue = ((Boolean)value) ? "Y" : "N";
return put(stringKey,stringValue);
}
#Override
public synchronized String put (String key, String value)
{
if (key == null)
return null;
if (value == null)
return remove(key);
//
int index = m_keys.indexOf(key);
if (index != -1)
return m_values.set(index, value);
m_keys.add(key);
m_values.add(value);
return null;
} // put
}
Both methods are used by this app a lot, I can't change the name of any and I can't delete put(Object key, Object value) method, any ideas how I can fix this?
#Aris_Kortex well firts of all, VO is used a LOT, to create the UImodel of this application and the constructor of VO is this:
public VO (Map<String,String> map)
{
this();
Iterator<String> it = map.keySet().iterator();
while (it.hasNext())
{
Object key = it.next().toString();
Object value = map.get(key);
put (key, value);
}
} // VO
/** Base SerVersion */
private static final long serialVersionUID = 8683452581122892189L;
/** Keys */
private ArrayList<String> m_keys;
/** Values */
private ArrayList<String> m_values;
/** Embedded VOs */
protected ArrayList<VO> p_vos = null;
then i am affraid i can't measure the impact of changing something here...i need to create a package of this proyect that i refactoriced because before was a mess..but maven throw me that error...
Related
I have a UserProfile class which contains user's data as shown below:
class UserProfile {
private String userId;
private String displayName;
private String loginId;
private String role;
private String orgId;
private String email;
private String contactNumber;
private Integer age;
private String address;
// few more fields ...
// getter and setter
}
I need to count non null fields to show how much percentage of the profile has been filled by the user. Also there are few fields which I do not want to consider in percentage calculation like: userId, loginId and displayName.
Simple way would be to use multiple If statements to get the non null field count but it would involve lot of boiler plate code and there is another class Organization for which I need to show completion percentage as well. So I created a utility function as show below:
public static <T, U> int getNotNullFieldCount(T t,
List<Function<? super T, ? extends U>> functionList) {
int count = 0;
for (Function<? super T, ? extends U> function : functionList) {
count += Optional.of(t).map(obj -> function.apply(t) != null ? 1 : 0).get();
}
return count;
}
And then I call this function as shown below:
List<Function<? super UserProfile, ? extends Object>> functionList = new ArrayList<>();
functionList.add(UserProfile::getAge);
functionList.add(UserProfile::getAddress);
functionList.add(UserProfile::getEmail);
functionList.add(UserProfile::getContactNumber);
System.out.println(getNotNullFieldCount(userProfile, functionList));
My question is, is this the best way I could count not null fields or I could improve it further. Please suggest.
You can simply a lot your code by creating a Stream over the given list of functions:
public static <T> long getNonNullFieldCount(T t, List<Function<? super T, ?>> functionList) {
return functionList.stream().map(f -> f.apply(t)).filter(Objects::nonNull).count();
}
This will return the count of non-null fields returned by each function. Each function is mapped to the result of applying it to the given object and null fields are filtered out with the predicate Objects::nonNull.
I wrote a utility class to get the total count of readable properties and the count of non null values in an object. The completion percentage can be calculated based on these.
It should work pretty well with inherited properties, nested properties, (multi-dimensional) iterables and maps.
I couldn't include the tests as well in here, because of the character limit, but here's the utility class:
import lombok.*;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
public class PropertyCountUtils {
/***
* See {#link #getReadablePropertyValueCount(Object, Set)}.
*/
public static PropertyValueCount getReadablePropertyValueCount(#NonNull Object object) {
return getReadablePropertyValueCount(object, null);
}
/**
* Counts the properties of the given object, including inherited and nested properties,
* returning the total property count and the count of properties with assigned values.
*
* <p>
* Properties with assigned values have a value meeting all conditions below:
* <ul>
* <li>different from null</li>
* <li>different from an empty iterable or an empty map</li>
* <li>different from an iterable containing only null values</li>
* <li>different from a map containing only null values.</li>
* </ul>
* For multidimensional Iterables and Maps, these conditions apply to each dimension.
* </p>
*
* #param object The object to inspect. It should not be null.
* #param ignoredProperties The properties to ignore or null.
* For nested properties, use dot as a separator: "property1.nestedProperty.nestedProperty2"
* #return A pair of `assignedValueCount` (properties with assigned value) and `totalCount` (total property count).
*/
public static PropertyValueCount getReadablePropertyValueCount(
#NonNull Object object, Set<String> ignoredProperties) {
PropertyValueCount countHolder = new PropertyValueCount();
processReadablePropertyValueCount(countHolder, object, ignoredProperties, null);
return countHolder;
}
/***
* #return true if the object had at least one non-null property value or no readable properties.
* <p>
* If the object is an instance of String, for example, it would have no readable nested properties.
* Also, if the object is an instance of some class for which all nested properties are ignored,
* the method would return true, since the object itself has a non-null value,
* but the caller decided to ignore all properties.
* </p>
*/
#SneakyThrows
private static boolean processReadablePropertyValueCount(
PropertyValueCount countHolder, #NonNull Object object, Set<String> ignoredProperties, String parentPath) {
boolean objectHasAssignedProperties = false;
boolean objectHasNoReadableProperties = true;
List<Field> fields = getAllDeclaredFields(object.getClass());
for (Field field : fields) {
String fieldPath = buildFieldPath(parentPath, field);
Method readMethod = getReadMethod(object.getClass(), ignoredProperties, field, fieldPath);
if (readMethod == null) {
continue;
}
countHolder.setTotalCount(countHolder.getTotalCount() + 1);
objectHasNoReadableProperties = false;
Object value = readMethod.invoke(object);
if (value == null || isCollectionWithoutAnyNonNullValue(value)) {
// no assigned value, so we'll just count the total available properties
int readablePropertyValueCount = getReadablePropertyCount(
readMethod.getGenericReturnType(), ignoredProperties, fieldPath);
countHolder.setTotalCount(countHolder.getTotalCount() + readablePropertyValueCount);
} else if (value instanceof Iterable<?> iterable) {
processPropertyValueCountInIterable(countHolder, ignoredProperties, fieldPath, iterable);
} else if (value instanceof Map<?, ?> map) {
processPropertyValueCountInIterable(countHolder, ignoredProperties, fieldPath, map.values());
} else {
countHolder.setAssignedValueCount(countHolder.getAssignedValueCount() + 1);
// process properties of nested object
processReadablePropertyValueCount(countHolder, value, ignoredProperties, fieldPath);
objectHasAssignedProperties = true;
}
}
return objectHasAssignedProperties || objectHasNoReadableProperties;
}
private static void processPropertyValueCountInIterable(
PropertyValueCount countHolder, Set<String> ignoredProperties, String fieldPath, Iterable<?> iterable) {
boolean iterableHasNonNullValues = false;
// process properties of each item in the iterable
for (Object value : iterable) {
if (value != null) {
// check if the current iterable item is also an iterable itself
Optional<Iterable<?>> nestedIterable = getProcessableCollection(value);
if (nestedIterable.isPresent()) {
processPropertyValueCountInIterable(countHolder, ignoredProperties, fieldPath, nestedIterable.get());
} else {
iterableHasNonNullValues = processReadablePropertyValueCount(
countHolder, value, ignoredProperties, fieldPath);
}
}
}
// consider the iterable as having an assigned value only if it contains at least one non-null value
if (iterableHasNonNullValues) {
countHolder.setAssignedValueCount(countHolder.getAssignedValueCount() + 1);
}
}
#SneakyThrows
private static int getReadablePropertyCount(
#NonNull Type inspectedType, Set<String> ignoredProperties, String parentPath) {
int totalReadablePropertyCount = 0;
Class<?> inspectedClass = getTargetClassFromGenericType(inspectedType);
List<Field> fields = getAllDeclaredFields(inspectedClass);
for (Field field : fields) {
String fieldPath = buildFieldPath(parentPath, field);
Method readMethod = getReadMethod(inspectedClass, ignoredProperties, field, fieldPath);
if (readMethod != null) {
totalReadablePropertyCount++;
Class<?> returnType = getTargetClassFromGenericType(readMethod.getGenericReturnType());
// process properties of nested class, avoiding infinite loops
if (!hasCircularTypeReference(inspectedClass, returnType)) {
int readablePropertyValueCount = getReadablePropertyCount(
returnType, ignoredProperties, fieldPath);
totalReadablePropertyCount += readablePropertyValueCount;
}
}
}
return totalReadablePropertyCount;
}
// In case the object being analyzed is of parameterized type,
// we want to count the properties in the class of the parameter, not of the container.
private static Class<?> getTargetClassFromGenericType(Type type) {
if (type instanceof ParameterizedType parameterizedType) {
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
if (actualTypeArguments.length > 0) {
// Inspect the last parameter type.
// For example, lists would only have one parameter type,
// but in the case of maps we would inspect the parameter representing the entry value, not the entry key.
Type inspectedTypeArgument = actualTypeArguments[actualTypeArguments.length - 1];
return inspectedTypeArgument instanceof ParameterizedType ?
getTargetClassFromGenericType(inspectedTypeArgument) :
(Class<?>) inspectedTypeArgument;
}
}
return type instanceof Class<?> ? (Class<?>) type : type.getClass();
}
private static List<Field> getAllDeclaredFields(#NonNull Class<?> inspectedClass) {
List<Field> fields = new ArrayList<>();
Collections.addAll(fields, inspectedClass.getDeclaredFields());
Class<?> superClass = inspectedClass.getSuperclass();
while (superClass != null) {
Collections.addAll(fields, superClass.getDeclaredFields());
superClass = superClass.getSuperclass();
}
return fields;
}
private static Method getReadMethod(#NonNull Class<?> inspectedClass, Set<String> ignoredProperties, Field field, String fieldPath) {
if (ignoredProperties != null && ignoredProperties.contains(fieldPath)) {
return null;
}
PropertyDescriptor propertyDescriptor;
try {
propertyDescriptor = new PropertyDescriptor(field.getName(), inspectedClass);
} catch (IntrospectionException e) {
// statement reached when the field doesn't have a getter
return null;
}
return propertyDescriptor.getReadMethod();
}
private static boolean hasCircularTypeReference(Class<?> propertyContainerClass, Class<?> propertyType) {
return propertyContainerClass.isAssignableFrom(propertyType);
}
private static String buildFieldPath(String parentPath, Field field) {
return parentPath == null ? field.getName() : parentPath + "." + field.getName();
}
private static boolean isCollectionWithoutAnyNonNullValue(Object value) {
Stream<?> stream = null;
if (value instanceof Iterable<?> iterable) {
stream = StreamSupport.stream(iterable.spliterator(), false);
} else if (value instanceof Map<?, ?> map) {
stream = map.values().stream();
}
return stream != null &&
stream.noneMatch(item -> item != null && !isCollectionWithoutAnyNonNullValue(item));
}
private static Optional<Iterable<?>> getProcessableCollection(Object value) {
if (value instanceof Iterable<?> iterable) {
return Optional.of(iterable);
} else if (value instanceof Map<?, ?> map) {
return Optional.of(map.values());
}
return Optional.empty();
}
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder
public static class PropertyValueCount {
private int assignedValueCount;
private int totalCount;
}
}
The completion percentage can be calculated like this:
PropertyCountUtils.PropertyValueCount propertyValueCount = getReadablePropertyValueCount(profile);
BigDecimal profileCompletionPercentage = BigDecimal.valueOf(propertyValueCount.getNonNullValueCount())
.multiply(BigDecimal.valueOf(100))
.divide(BigDecimal.valueOf(propertyValueCount.getTotalCount()), 2, RoundingMode.UP)
.stripTrailingZeros();
There is an useful class in android (at least for me). The Bundle class which allows you to put a kind of pair Key Value. But the value can be a different established classes of objects.
For example:
Bundle mBundle = new Bundle();
mBundle.putInt("Number One", 1);
mBundle.putBoolean("True", true);
There is a similar class in java?
Not that I know of, but it's fairly easy to reproduce with a HashMap:
public class Bundle extends HashMap<String,Object>{
public void putString(String key, String str){
put(key, str);
}
public String getString(String key){
Object o = get(key);
if(o!=null && o instanceof String)
return (String) o;
return null;
}
public void putInteger(String key, int anInt){
put(key, new Integer(anInt));
}
public Integer getInteger(String key){
Object o = get(key);
if(o!=null && o instanceof Integer)
return (Integer) o;
return null;
}
...
}
I don't think Java contains this kind of Object by default but you can create one:
public class Bundle extends HashMap<String, Object> {
public void putInt(String key, int value) {
put(key, value);
}
public void putString(String key, String value) {
put(key, value);
}
public Integer getInt(String key) {
if (containsKey(key) && get(key) instanceof Integer) {
return (Integer) get(key);
} else {
return null;
}
}
public String getString(String key) {
if (containsKey(key) && get(key) instanceof String) {
return (String) get(key);
} else {
return null;
}
}
}
This question already has answers here:
HashMap to return default value for non-found keys?
(16 answers)
Closed 9 years ago.
While using Map as a function argument, only values for 3 keys are populated. However when this function is invoked in another function, the user populates values for initial 2 keys and he does not require 3rd key to be assigned with any value. However if 3rd key is not assigned any value then, the 3rd key is display null value.
Is there any way to avoid this. if user does not assign any value to 3rd key, it must be empty instead of null value.
public String setMapValues(Map<String,String> testMap) throws Exception
{
String str="";
str= testMap.get("a");
str+=testMap.get("b");
str+=testMap.get("c");
info(str);
return str;
}
public void run() throws Exception
{
LinkedHashMap<String,String> myMap = new LinkedHashMap<String,String>();
myMap.put("a", "James");
myMap.put("b", "Bond");
this.setMapValues(myMap);
}
The function calls displays JamesBondnull as the output, instead it should only display JamesBond as the output by ignoring/skipping the null at the end.
You can use a function like
static String nullAsEmpty(Object o) {
return o == null ? "" : o.toString();
}
public String setMapValues(Map<String,String> testMap) {
String str = nullAsEmpty(testMap.get("a")) +
nullAsEmpty(testMap.get("b")) +
nullAsEmpty(testMap.get("c"));
info(str);
return str;
}
How about:
String temp = testMap.get("c");
str+= (temp == null : "" : temp);
You can implement your version of Map:
import java.util.HashMap;
class MyMap<K, V> extends HashMap<K, V> {
#Override
public V get(Object key) {
V val = super.get(key);
if (val != null) {
return val;
} else {
return "";
}
}
}
Then just use MyMap instead of Map
Or init your map with default values, if you know all keys which could be null
Map getInstance(){
Map<String,String> myMap = new LinkedHashMap<String,String>();
myMap.put("a", "");
myMap.put("b", "");
myMap.put("b", "");
return myMap;
}
By putting duplicate keys the old values are replaced by the new ones.
To be complete:
If you use Vadchens answer - which is better - you can do two things:
Extends your map by setting generic value-parameter directly to String
class MySecMap<K> extends LinkedHashMap<K, String>{
#Override
public String get(Object key) {
String val = super.get(key);
if (val != null) {
return val;
} else {
return "";
}
}
}
Or create a class with an extra interface and a default-value-provider:
interface IDefaultValueProvider<V>{
V getDefaultValue();
}
class MyMap<K, V, D extends IDefaultValueProvider<V>> extends LinkedHashMap<K, V>{
private IDefaultValueProvider<V> provider;
public MyMap(IDefaultValueProvider<V> p){
super();
provider = p;
}
#Override
public V get(Object key) {
V val = super.get(key);
if (val != null) {
return val;
} else {
return this.provider.getDefaultValue();
}
}
}
Can anyone suggest me why the if condition is not working in the below code as the record has key as SiteId.
while (!pdsxOutRecords.isEmpty()) {
PdsxRecord record = pdsxOutRecords.remove(0);
// The below if condition is not working
if(record.getAttrs().containsKey("SiteId")) {
System.out.println("Testing");
}
}
And PdsxRecord class is like this
public class PdsxRecord
{
private String m_key;
private Map<PdsxAttrKey, PdsxAttrValue> m_mapAttrs;
}
// constructor
public PdsxRecord(String key, Map<PdsxAttrKey, PdsxAttrValue> mapAttrs)
{
m_key = key;
m_mapAttrs = mapAttrs;
}
public String getKey()
{
return m_key;
}
public Map<PdsxAttrKey, PdsxAttrValue> getAttrs()
{
return m_mapAttrs;
}
Below thing gets printed by using record.getAttrs()
{Gem.2036=null, Gem.2037=null, Gem.2038=com.ebay.pdsx.common.PdsxAttrValue#6b306b30, Gem.2039=null, Gem.10230=null, Gem.10117=null, Gem.10119=null, Gem.10240=null, UID=com.ebay.pdsx.common.PdsxAttrValue#1e501e50, Gem.10001=null, Gem.10002=com.ebay.pdsx.common.PdsxAttrValue#5d095d09, Gem.10003=null, Gem.10246=null, Gem.10247=null, Gem.60001=null, Gem.10007=null, Gem.10009=null, GEM_ROUTING.PartnerLastModifiedDate=null, Gem.70006=null, CGUID=com.ebay.pdsx.common.PdsxAttrValue#1e361e36, Gem.10173=null, Gem.10097=null, Gem.10131=null, Gem.10010=null, Gem.10132=null, Gem.10177=null, Gem.10178=null, Gem.10179=null, Gem.10015=null, TimeStamp=com.ebay.pdsx.common.PdsxAttrValue#1e571e57, Gem.10016=com.ebay.pdsx.common.PdsxAttrValue#645e645e, Gem.10018=null, Gem.10019=null, Gem.2025=null, SiteId=com.ebay.pdsx.common.PdsxAttrValue#1e3f1e3f, GEM_ROUTING.Partner1LastLoggedInUserId=null, GEM_ROUTING.Partner3LastLoggedInUserId=null, Gem.10181=null, Gem.10182=null, Gem.10183=null, Gem.10185=null, Gem.10187=null, Gem.10101=null, Gem.10189=null, Gem.10102=null, Gem.10026=null, PGuid=com.ebay.pdsx.common.PdsxAttrValue#1e461e46, Gem.2032=null, SGuid=null, Gem.2033=null, Gem.2034=null, Gem.2035=null}
This is the below PdsxRecord class
public class PdsxRecord
{
private String m_key;
private Map<PdsxAttrKey, PdsxAttrValue> m_mapAttrs;
// use the other constructor!
protected PdsxRecord()
{
}
// constructor
public PdsxRecord(String key, Map<PdsxAttrKey, PdsxAttrValue> mapAttrs)
{
m_key = key;
m_mapAttrs = mapAttrs;
}
/**
* get Key
*
* #return
*/
public String getKey()
{
return m_key;
}
/**
* get attributes as a map of key=value
*
* #return
*/
public Map<PdsxAttrKey, PdsxAttrValue> getAttrs()
{
return m_mapAttrs;
}
/**
* String -- for debugging and simple persistence
*/
public String toString()
{
UnsynchronizedStringBuffer buf = new UnsynchronizedStringBuffer();
buf.append("key=" + getKey() + "\n");
if (getAttrs() == null || getAttrs().size() == 0) {
return buf.toString();
}
for (Map.Entry<PdsxAttrKey, PdsxAttrValue> entry : getAttrs().entrySet()) {
String key = (entry.getKey()==null ? "null" : entry.getKey().getKey());
String value = ((entry.getValue() == null ||
entry.getValue().getValue() == null) ?
"null" : entry.getValue().getValue().toString());
buf.append(" " + key + "=" + value +"\n");
}
return buf.toString();
}
}
Updated:- Class for PdsxAttrKey
public class PdsxAttrKey
{
private String m_key;
protected PdsxAttrKey()
{
}
public PdsxAttrKey(String key)
{
m_key = key;
}
public String getKey()
{
return m_key;
}
/**
* Override the default to allow comparing with Strings
*/
public boolean equals(Object o)
{
if (o == null) {
return false;
}
if (o instanceof String) {
return o.equals(m_key);
}
if (o instanceof PdsxAttrKey) {
return m_key.equals(((PdsxAttrKey)o).getKey());
}
return false;
}
/**
* hash code implementation
*/
public int hashCode()
{
return (m_key == null ? 0 : m_key.hashCode());
}
public String toString()
{
return getKey();
}
}
Maybe because you Map is consisting of PdsxAttrKey as a key, and you are checking if there's a key which is a String with value "SiteId".
Here's some code that might be useful if you do not want to change your Map definition from Map<PdsxAttrKey, PdsxAttrValue> to something like Map<String, PdsxAttrValue>:
while (!pdsxOutRecords.isEmpty()) {
PdsxRecord record = pdsxOutRecords.remove(0);
if(record.getAttrs().containsKey(new PdsxAttrKey("SiteId"))) {
System.out.println("Testing");
}
}
Note that this assumes that you can pass a String to the PdsxAttrKey constructor, and that the class can be instantiated. Oh and of course that you have equals() and hashcode() in the class, which pretty much only check the String value for the of the PdsxAttrKey. You might ask yourself if this is really worth the hassle. That's why I originally suggested that your change your Map definition to use Strings as keys, but of course I am not sure if this is a viable solution in your case.
In record.getAttrs() you're returning: Map<PdsxAttrKey, PdsxAttrValue>. Then you checking if there is a key (type: PdsxAttrKey) of type String, value "SiteId".
You should check if map contains PdsxAttrKey (implementing equals and hashcode methods in PdsxAttrKey) or extracting keys from PdsxAttrKey and comparing them with "SiteId".
If you choose to iterate try this:
for(PdsxAttrKey key : record.getAttrs().keySet()) {
if("SiteId".equals(key.getYourKeyStringValue())) {
//found
break;
}
}
Otherwise you should implement equals and hashcode (remember - both) in PdsxAttrKey and invoke contains:
PdsxAttrKey lookupKey = new PdsxAttrKey("SiteId"); //with consideration of `equals` method
if(record.getAttrs().containsKey(lookupKey)) {
...
}
As Xeon says, if you want to compare object with string value, you must override equals and hascode method in the class which is used as key, in this case, PdsxAttrKey. As an example:
public class PdsxAttrKey {
public String name;
public PdsxAttrKey(String name) {
this.name = name;
}
#Override
public int hashCode() {
return name.hashCode();
}
#Override
public boolean equals(Object obj) {
if(obj == null) {
return false;
} else if (obj instanceof PdsxAttrKey) {
return this.name.equals(((PdsxAttrKey)obj).name);
}
return false;
}
}
Or If there is no real need to have an object as key, then you can redefine the map declaration as follows and use strings as key.
private Map<String, PdsxAttrValue> m_mapAttrs;
I have a strange issue occuring in my application, I will quickly explain global architecture and then my problem in depth.
I use a service to populate a HashMap<DomainObject,Boolean> coming from my database (JPA driven) which is in turn returned to my view, via an EJB remote method call (using Apache Wicket). In this part, I add a new DomainObject to the map returned in order to store any new value from my end user.
The problem occurs when the user hit the "add" button in its browser, I try to retrieve the newly created item in my map, but it fails. By playing with the debugger I face the following things.
Assuming HashMap<DomainObject, Boolean> map and DomainObject do are the two variables interesting I have the following results in the debugger
map.keySet(); gives me an object corresponding to do (even the #whatever simili-reference is identical), hashcode() on both objects returns similar value and equals() between the two returns true
map.containsKey(do); returns false
map.get(do); returns null, weird because my key seems to be in the map.
Assuming my newly created item is the first key enumerated by keySet(), I do the following :
map.get(new ArrayList(map.keySet()).get(0)), and it returns null.
If it can help, by attaching breakpoints to my DomainObject.equals() and DomainObject.hashcode() methods I found that map.get() is only calling hashcode() and not equals().
The only workaround I found is to recreate a new map on top of the existing one new HashMap(map), in this new map, I have no problem at all looking up an object by its key.
I hope someone here can give my a pointer on what happens, thanks.
Environment used :
Sun Java 1.6.0_26 x64 under OS X 10.7.1
OpenJDK 1.6.0_18 x64 under Debian 6.0.2 (2.6.32)
Apache Wicket 1.4.17
Oracle Glassfish 3.1.1
JBoss Hibernate 3.6.5
DomainObject code :
public class AssetComponentDetailTemplate extends BaseEntite<Long> {
public enum DataType {
TXT,
DATE,
INT,
JOIN,
LIST,
COULEURS,
REFERENCE
}
public enum Tab {
IDENTITE,
LOCALISATION,
CYCLE_DE_VIE,
FINANCE,
RESEAU,
DETAIL
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(nullable = false)
private String name;
#Column(nullable = false)
#Enumerated(EnumType.STRING)
private DataType dataType;
private Integer classNameId;
private Long orderId;
private Long nextAssetComponentDetailTemplateId;
private String unit;
#Enumerated(EnumType.STRING)
private Tab tab;
#Column(nullable = false)
private Long uniqueOrganizationId;
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name = "idAssetComponentDetailTemplate", insertable = false, updatable = false)
private List<AssetComponentDetailJoin> assetComponentDetailJoins;
private Boolean mandatory = false;
public AssetComponentDetailTemplate() {
}
public Long getId() {
return id;
}
public void setId(final Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
public DataType getDataType() {
return dataType;
}
public void setDataType(final DataType dataType) {
this.dataType = dataType;
}
public Integer getClassNameId() {
return classNameId;
}
public void setClassNameId(final Integer classNameId) {
this.classNameId = classNameId;
}
public Long getUniqueOrganizationId() {
return uniqueOrganizationId;
}
public void setUniqueOrganizationId(final Long uniqueOrganizationId) {
this.uniqueOrganizationId = uniqueOrganizationId;
}
public Long getNextAssetComponentDetailTemplateId() {
return nextAssetComponentDetailTemplateId;
}
public void setNextAssetComponentDetailTemplateId(final Long nextAssetComponentDetailTemplateId) {
this.nextAssetComponentDetailTemplateId = nextAssetComponentDetailTemplateId;
}
public String getUnit() {
return unit;
}
public void setUnit(final String unit) {
this.unit = unit;
}
public Tab getTab() {
return tab;
}
public void setTab(final Tab tab) {
this.tab = tab;
}
public Long getOrder() {
return orderId;
}
public void setOrder(final Long order) {
this.orderId = order;
}
public Boolean isMandatory() {
return mandatory;
}
#Override
public String toString() {
return name;
}
#Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final AssetComponentDetailTemplate that = (AssetComponentDetailTemplate) o;
if (classNameId != null ? !classNameId.equals(that.classNameId) : that.classNameId != null) {
return false;
}
if (dataType != that.dataType) {
return false;
}
if (id != null ? !id.equals(that.id) : that.id != null) {
return false;
}
if (name != null ? !name.equals(that.name) : that.name != null) {
return false;
}
if (nextAssetComponentDetailTemplateId != null ?
!nextAssetComponentDetailTemplateId.equals(that.nextAssetComponentDetailTemplateId) :
that.nextAssetComponentDetailTemplateId != null) {
return false;
}
if (orderId != null ? !orderId.equals(that.orderId) : that.orderId != null) {
return false;
}
if (tab != that.tab) {
return false;
}
if (uniqueOrganizationId != null ? !uniqueOrganizationId.equals(that.uniqueOrganizationId) :
that.uniqueOrganizationId != null) {
return false;
}
if (unit != null ? !unit.equals(that.unit) : that.unit != null) {
return false;
}
return true;
}
#Override
public int hashCode() {
int result = id != null ? id.hashCode() : 0;
result = 31 * result + (name != null ? name.hashCode() : 0);
result = 31 * result + (dataType != null ? dataType.hashCode() : 0);
result = 31 * result + (classNameId != null ? classNameId.hashCode() : 0);
result = 31 * result + (orderId != null ? orderId.hashCode() : 0);
result = 31 * result +
(nextAssetComponentDetailTemplateId != null ? nextAssetComponentDetailTemplateId.hashCode() : 0);
result = 31 * result + (unit != null ? unit.hashCode() : 0);
result = 31 * result + (tab != null ? tab.hashCode() : 0);
result = 31 * result + (uniqueOrganizationId != null ? uniqueOrganizationId.hashCode() : 0);
return result;
}
[This basically expands on Jesper's answer but the details may help you]
Since recreating the map using new HashMap(map) is able to find the element I am suspecting that the hashCode() of the DomainObject changed after adding it to the Map.
For example if your DomainObject looks the following
class DomainObject {
public String name;
long hashCode() { return name.hashCode(); }
boolean equals(Object other) { /* compare name in the two */'
}
Then
Map<DomainObject, Boolean> m = new HashMap<DomainObject, Boolean>();
DomainObject do = new DomainObject();
do.name = "ABC";
m.put(do, true); // do goes in the map with hashCode of ABC
do.name = "DEF";
m.get(do);
The last statement above will return null. Because the do object you have inside the map is under the bucket of "ABC".hashCode(); there is nothing in the "DEF".hashCode() bucket.
The hashCode of the Objects in map should not change once added to map. The best way to ensure it is that the fields on which hashCode depends must be immutable.
Is your DomainObject class immutable? Does it have properly implemented hashCode and equals methods?
Note that you will get into trouble if your DomainObject class is not immutable and you change the state of the object while it is in the map in a way that would change the result of calling hashCode or equals.
hashCode must be implemented in such a way that it returns the same value for two objects whenever equals returns true when comparing these objects. See the API documentation of java.lang.Object.hashCode() for detailed information.
Here is your clue:
hashcode() on both objects returns similar value
For the objects to be considered equal, their hash codes shouldn't just be similar, they must be identical.
If two objects have different hash codes, then as far as the container is concerned the objects are different. There's no need to even call equals().
From the Javadoc:
The general contract of hashCode is:
If two objects are equal according to the equals(Object) method, then
calling the hashCode method on each of the two objects must produce
the same integer result.
If I were you, I'd take a close look at DomainObject.hashcode() and DomainObject.equals() to see what's causing the contract to be broken.
map.get(do) returning null could be easily explained by assuming that the Boolean value for that key might be null but map.containsKey(do) returning false would require do's hashCode to be different at the time of calling containsKey(do) to it's hashCode at the time of retrieving it from the keySet.
To see what's happening, you could (temporarily) use a more verbose implementation of HashMap...
Maybe something like this:
public class VerboseHashMap<K, V> implements Map<K, V> {
private transient final static Logger logger = Logger.getLogger(VerboseHashMap.class);
private HashMap<K, V> internalMap = new HashMap<K, V>();
public boolean containsKey(Object o) {
logger.debug("Object HashCode: " + o.hashCode());
logger.debug("Map contents:");
for (Entry<K, V> entry : internalMap.entrySet()) {
logger.debug(entry.getKey().hashCode() + " - " + entry.getValue().toString());
}
return internalMap.containsKey(o);
}
public V get(Object key) {
logger.debug("Object HashCode: " + key.hashCode());
logger.debug("Map contents:");
for (Entry<K, V> entry : internalMap.entrySet()) {
logger.debug(entry.getKey().hashCode() + " - " + entry.getValue().toString());
}
return internalMap.get(key);
}
}
You'd need to map all the other requirements of the Map interface to your internalMap as well.
Note: This code is not intended for production, nor is it in any way performance oriented, nice or unsmelly....
2nd note (after seeing your code): To use your domain-object as a key for your hashMap, you should only use the immutable parts of your object for hashCode and equals (in this case the id-value). Else lazy-loading further values would change the hashCode...
In Response to your comment:
public class Demo extends TestCase {
public void testMap() {
Map<DomainObject, String> map = new HashMap<DomainObject, String>();
DomainObject sb = new DomainObject();
map.put(sb, "Some value");
System.out.println(map.containsKey(sb));
sb.value = "Some Text";
System.out.println(map.containsKey(sb));
}
private static class DomainObject {
public String value = null;
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((value == null) ? 0 : value.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
DomainObject other = (DomainObject) obj;
if (value == null) {
if (other.value != null)
return false;
} else if (!value.equals(other.value))
return false;
return true;
}
}
}
prints
true
false
The HashCode for the key is computed at the time of putting it into the map.