Compare objects by multiple fields in Java - dict method in Python - java

In python we can compare object by attributes by this method:
def __eq__(self, other):
return self.__dict__ == other.__dict__
It allows us to compare objects by their attributes no matter how many attributes they have.
Is something like this possible in Java?
I tried to compare objects in this way, but you have to compare them by every attribute. Is it possible to do it similarly to Python eq method above?
public class MyClass {
public MyClass(attr1, attr2, attr3, attr4) {
this.attr1 = attr1;
this.attr2 = attr2;
this.attr3 = attr3;
this.attr4 = attr4;
}
public boolean equals(Object object2) {
return object2 instanceof MyClass &&
attr1.equals(((MyClass)object2).attr1)&&......;
}
}

If you want to write general method that handles every type, you need to use reflection. If you just want to do this with a type or two, then I suggest you override the equals method for each individual type i.e. hardcoding it.
Here's how you write a generic method that accepts every single type and compares the fields for equality:
private static <T> boolean allFieldsEqual(T a, T b) throws IllegalAccessException {
Class<?> clazz = a.getClass();
Field[] fields = clazz.getFields();
for (Field field : fields) {
if (!field.get(a).equals(field.get(b))) {
return false;
}
}
return true;
}
The logic is pretty self-explanatory. Here is the usage:
Student s1 = new Student("Tom", 1);
Student s2 = new Student("Tom", 1);
try {
System.out.println(allFieldsEqual(s1, s2));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
Student is defined as:
public class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}

Related

Object to string delimited format

I have set of objects of different types.
Ex : Employee emp, adress adr
These two classes have list of properties
public class Employee{
private Stringname;
private int age;
}
public class Adress {
private String HouseNo;
private string Street;
private string pin;
}
Each attribute is assigned with some 2 character value
Name (NA), age (AG), HouseNo(HN),Street(ST), pin(PN)
I need to construct a string with these data and delimit with a %
Output:
NA%Vidhya%AG%30%HN%80%ST%1st cross%PN%100100
Each class knows it own data best so I would let each class be responsible for generating the string. As I understand it the two char codes for each field are unique for each class and member and only used when generating the string so only the class would need them.
interface AttributeDescription {
String generateDescription();
}
public class Employee implements AttributeDescription {
//members...
public String generateDescription() {
return String.format(“NA%%%s%%AG%%%d”, name, age)
}
Then simply call this method for all objects implementing the interface.
AttributeDescription object = ...
String attr = object.generateDescription();
I don't think it can be generalized more than this given the requirements.
Update
It might be better to have a builder class for building the string to get a more unified behavior between classes. Here is an example
public class AttributeBuilder {
private builder = new StringBuilder();
public String getAttribute() {
return builder.toString();
}
public void add(String code, String value) {
if (value == null) {
return;
}
builder.append(code);
builder.append(‘%’);
builder.append(value);
builder.append(‘%’);
}
}
And then you would also have to implement add(...) methods for other data types in a similar fashion. The builder could then be used like
public String generateDescription() {
AttributeBuilder builder = new AttributeBuilder();
builder.add(“NA”, name);
builder.add(“AG”, age);
return builder.getAttribute();
}

Is there any default method for ENUM? [duplicate]

What is the best way to use the values stored in an Enum as String literals?
For example:
public enum Modes {
some-really-long-string,
mode1,
mode2,
mode3
}
Then later I could use Mode.mode1 to return its string representation as mode1. Without having to keep calling Mode.mode1.toString().
You can't. I think you have FOUR options here. All four offer a solution but with a slightly different approach...
Option One: use the built-in name() on an enum. This is perfectly fine if you don't need any special naming format.
String name = Modes.mode1.name(); // Returns the name of this enum constant, exactly as declared in its enum declaration.
Option Two: add overriding properties to your enums if you want more control
public enum Modes {
mode1 ("Fancy Mode 1"),
mode2 ("Fancy Mode 2"),
mode3 ("Fancy Mode 3");
private final String name;
private Modes(String s) {
name = s;
}
public boolean equalsName(String otherName) {
// (otherName == null) check is not needed because name.equals(null) returns false
return name.equals(otherName);
}
public String toString() {
return this.name;
}
}
Option Three: use static finals instead of enums:
public final class Modes {
public static final String MODE_1 = "Fancy Mode 1";
public static final String MODE_2 = "Fancy Mode 2";
public static final String MODE_3 = "Fancy Mode 3";
private Modes() { }
}
Option Four: interfaces have every field public, static and final:
public interface Modes {
String MODE_1 = "Fancy Mode 1";
String MODE_2 = "Fancy Mode 2";
String MODE_3 = "Fancy Mode 3";
}
Every enum has both a name() and a valueOf(String) method. The former returns the string name of the enum, and the latter gives the enum value whose name is the string. Is this like what you're looking for?
String name = Modes.mode1.name();
Modes mode = Modes.valueOf(name);
There's also a static valueOf(Class, String) on Enum itself, so you could also use:
Modes mode = Enum.valueOf(Modes.class, name);
You could override the toString() method for each enum value.
Example:
public enum Country {
DE {
#Override
public String toString() {
return "Germany";
}
},
IT {
#Override
public String toString() {
return "Italy";
}
},
US {
#Override
public String toString() {
return "United States";
}
}
}
Usage:
public static void main(String[] args) {
System.out.println(Country.DE); // Germany
System.out.println(Country.IT); // Italy
System.out.println(Country.US); // United States
}
As Benny Neugebauer mentions, you could overwrite the toString(). However instead overwriting the toString for each enum field I like more something like this:
public enum Country{
SPAIN("España"),
ITALY("Italia"),
PORTUGAL("Portugal");
private String value;
Country(final String value) {
this.value = value;
}
public String getValue() {
return value;
}
#Override
public String toString() {
return this.getValue();
}
}
You could also add a static method to retrieve all the fields, to print them all, etc.
Simply call getValue to obtain the string associated to each Enum item
mode1.name() or String.valueOf(mode1). It doesn't get better than that, I'm afraid
public enum Modes {
MODE1("Mode1"),
MODE2("Mode2"),
MODE3("Mode3");
private String value;
public String getValue() {
return value;
}
private Modes(String value) {
this.value = value;
}
}
you can make a call like below wherever you want to get the value as a string from the enum.
Modes.MODE1.getvalue();
This will return "Mode1" as a String.
For my enums I don't really like to think of them being allocated with 1 String each. This is how I implement a toString() method on enums.
enum Animal
{
DOG, CAT, BIRD;
public String toString(){
switch (this) {
case DOG: return "Dog";
case CAT: return "Cat";
case BIRD: return "Bird";
}
return null;
}
}
You can use Mode.mode1.name() however you often don't need to do this.
Mode mode =
System.out.println("The mode is "+mode);
As far as I know, the only way to get the name would be
Mode.mode1.name();
If you really need it this way, however, you could do:
public enum Modes {
mode1 ("Mode1"),
mode2 ("Mode2"),
mode3 ("Mode3");
private String name;
private Modes(String s) {
name = s;
}
}
my solution for your problem!
import java.util.HashMap;
import java.util.Map;
public enum MapEnumSample {
Mustang("One of the fastest cars in the world!"),
Mercedes("One of the most beautiful cars in the world!"),
Ferrari("Ferrari or Mercedes, which one is the best?");
private final String description;
private static Map<String, String> enumMap;
private MapEnumSample(String description) {
this.description = description;
}
public String getEnumValue() {
return description;
}
public static String getEnumKey(String name) {
if (enumMap == null) {
initializeMap();
}
return enumMap.get(name);
}
private static Map<String, String> initializeMap() {
enumMap = new HashMap<String, String>();
for (MapEnumSample access : MapEnumSample.values()) {
enumMap.put(access.getEnumValue(), access.toString());
}
return enumMap;
}
public static void main(String[] args) {
// getting value from Description
System.out.println(MapEnumSample.getEnumKey("One of the fastest cars in the world!"));
// getting value from Constant
System.out.println(MapEnumSample.Mustang.getEnumValue());
System.out.println(MapEnumSample.getEnumKey("One of the most beautiful cars in the world!"));
System.out.println(MapEnumSample.Mercedes.getEnumValue());
// doesnt exist in Enum
System.out.println("Mustang or Mercedes, which one is the best?");
System.out.println(MapEnumSample.getEnumKey("Mustang or Mercedes, which one is the best?") == null ? "I don't know!" : "I believe that "
+ MapEnumSample.getEnumKey("Ferrari or Mustang, which one is the best?") + " is the best!.");
// exists in Enum
System.out.println("Ferrari or Mercedes, wich one is the best?");
System.out.println(MapEnumSample.getEnumKey("Ferrari or Mercedes, which one is the best?") == null ? "I don't know!" : "I believe that "
+ MapEnumSample.getEnumKey("Ferrari or Mercedes, which one is the best?") + " is the best!");
}
}
You can simply use:
""+ Modes.mode1
public enum Environment
{
PROD("https://prod.domain.com:1088/"),
SIT("https://sit.domain.com:2019/"),
CIT("https://cit.domain.com:8080/"),
DEV("https://dev.domain.com:21323/");
private String url;
Environment(String envUrl) {
this.url = envUrl;
}
public String getUrl() {
return url;
}
}
String prodUrl = Environment.PROD.getUrl();
It will print:
https://prod.domain.com:1088/
This design for enum string constants works in most of the cases.
Enum is just a little bit special class. Enums can store additional fields, implement methods etc. For example
public enum Modes {
mode1('a'),
mode2('b'),
mode3('c'),
;
char c;
private Modes(char c) {
this.c = c;
}
public char character() {
return c;
}
}
Now you can say:
System.out.println(Modes.mode1.character())
and see output:
a
package com.common.test;
public enum Days {
monday(1,"Monday"),tuesday(2,"Tuesday"),wednesday(3,"Wednesday"),
thrusday(4,"Thrusday"),friday(5,"Friday"),saturday(6,"Saturday"),sunday(7,"Sunday");
private int id;
private String desc;
Days(int id,String desc){
this.id=id;
this.desc=desc;
}
public static String getDay(int id){
for (Days day : Days.values()) {
if (day.getId() == id) {
return day.getDesc();
}
}
return null;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
};
This method should work with any enum:
public enum MyEnum {
VALUE1,
VALUE2,
VALUE3;
public int getValue() {
return this.ordinal();
}
public static DataType forValue(int value) {
return values()[value];
}
public String toString() {
return forValue(getValue()).name();
}
}
i found this one is more easy for preventing type error:
public enum Modes {
some-really-long-string,
mode1,
mode2,
mode3;
String str;
Modes(){
this.str = super.name();
}
#Override
#NonNull
public String toString() {
return str;
}
however - this may work when you need to use a String on a log/println or whenever java compiles the toString() method automatically, but on a code line like this ->
// sample method that require (string,value)
intent.putExtra(Modes.mode1 ,shareElement.getMode()); // java error
// first argument enum does not return value
instead as mentioned above you will still have to extend the enum and use .name() in those cases like this:
intent.putExtra(Modes.mode1.name() ,shareElement.getMode());
after many tries I have come with this solution
public static enum Operation {
Addition, Subtraction, Multiplication, Division,;
public String getUserFriendlyString() {
if (this==Addition) {
return " + ";
} else if (this==Subtraction) {
return " - ";
} else if (this==Multiplication) {
return " * ";
} else if (this==Division) {
return " / ";
}
return "undefined";
}
}
You can try this:
public enum Modes {
some-really-long-string,
mode1,
mode2,
mode3;
public String toString(){
switch(this) {
case some-really-long-string:
return "some-really-long-string";
case mode2:
return "mode2";
default: return "undefined";
}
}
}
use mode1.name() or String.valueOf(Modes.mode1)

Where exactly do I need to avoid direct field access in the presence of Hibernate proxies?

I have a simple entity class as follows:
#Entity public class Foo {
#NotNull #Column private String name;
public String getName() { return this.name; }
public void setName(String name) { this.name = name; }
}
Now in the face of Hibernate proxies, I know I cannot rely on getClass() to return Foo.class, and that I cannot rely on instanceof in the face of entity hierarchies. And I know that I should not access other.name directly in the equals method. For instance, I shouldn't do this:
// in class Foo:
#Override public boolean equals(Object obj) {
// instanceof is fine in this case since Foo has no entity superclass
if (!(obj instanceof Foo)) {
return false;
}
Foo other = (Foo) obj;
// hold up! if other is a proxy, then other.name will be null
return Objects.equal(name, other.name);
}
My question is, where exactly is it not safe to access other.name? And is it ever unsafe to access this.name?
This is going in to production soon, and I don't have the luxury of a "try and see" approach here.
To answer the question, I jimmied up my Foo class to report on field access in different contexts. Here is what I added:
public String callGetNamePublic() {
System.out.print("callGetNamePublic: ");
return getNamePublic();
}
public String getNamePublic() {
System.out.println("getNamePublic: " + name);
return name;
}
public String callGetNameProtected() {
System.out.print("callGetNameProtected: ");
return getNameProtected();
}
protected String getNameProtected() {
System.out.println("getNameProtected: " + name);
return name;
}
public String callGetNamePackage() {
System.out.print("callGetNamePackage: ");
return getNamePackage();
}
String getNamePackage() {
System.out.println("getNamePackage: " + name);
return name;
}
public String callGetNamePrivate() {
System.out.print("callGetNamePrivate: ");
return getNamePrivate();
}
private String getNamePrivate() {
System.out.println("getNamePrivate: " + name);
return name;
}
public static class FooSubclass extends Foo {
public String callOtherGetNamePublic(Foo other) {
System.out.print("callOtherGetNamePublic: ");
return other.getNamePublic();
}
public String callOtherGetNameProtected(Foo other) {
System.out.print("callOtherGetNameProtected: ");
return other.getNameProtected();
}
public String callOtherGetNamePackage(Foo other) {
System.out.print("callOtherGetNamePackage: ");
return other.getNamePackage();
}
public String callOtherGetNamePrivate(Foo other) {
System.out.print("callOtherGetNamePrivate: ");
return other.getNamePrivate();
}
public String accessOtherName(Foo other) {
System.out.print("accessOtherName: " + other.name);
return other.name;
}
}
Then I rigged up a test to pull up a Foo proxy object, and call these methods. Like so:
// I'll leave this to your imagination ;)
Foo foo1 = getFooProxy();
foo1.getName();
foo1.getNamePublic();
foo1.callGetNamePublic();
foo1.callGetNameProtected();
foo1.callGetNamePackage();
foo1.callGetNamePrivate();
FooSubclass foo2 = new Foo.FooSubclass();
foo2.callOtherGetNamePublic(foo1);
foo2.callOtherGetNameProtected(foo1);
foo2.callOtherGetNamePackage(foo1);
foo2.callOtherGetNamePrivate(foo1);
foo2.accessOtherName(foo1);
Here are the results:
getNamePublic: testName
callGetNamePublic: getNamePublic: testName
callGetNameProtected: getNameProtected: testName
callGetNamePackage: getNamePackage: testName
callGetNamePrivate: getNamePrivate: testName
callOtherGetNamePublic: getNamePublic: testName
callOtherGetNameProtected: getNameProtected: testName
callOtherGetNamePackage: getNamePackage: testName
callOtherGetNamePrivate: getNamePrivate: null
accessOtherName: null
I've since discovered that you need to avoid direct field access in any final methods as well. If you change any of the following methods to final, and the corresponding results will change from testName to null:
getNamePublic
getNameProtected
getNamePackage
getNamePrivate
So the answer is: The only limitations to direct field access in the face of Hibernate proxies are (1) direct field access to other.name, (2) inside calls to private methods on other, and (3) inside any final methods.

get confused about the object, why I can not just use obj to getIdStr() instead of making a new personObj

I'm trying to make a my own equals method. But I'm confused about the obj in the code. In the part of equals method I must create a new personObj, in which way I can use getIdStr. Why I can not just use obj like obj.
class Person {
private String name;
private String idStr;
public Person() {
}
public Person(String name, String idStr) {
this.name = name;
this.idStr = idStr;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setIdStr(String idStr) {
this.idStr = idStr;
}
public String getIdStr() {
return idStr;
}
public boolean equals(Object obj) {
System.out.println("" + obj);
System.out.println("" + this);
if (this == obj)
return true;
if (obj != null && obj.getClass() == Person.class) {
Person personObj = (Person) obj;
System.out.println("" + obj.getClass());
System.out.println("" + personObj.getClass());
if (this.getIdStr().equals(personObj.getIdStr())) {
return true;
}
}
return false;
}
}
public class OverrideEqualsRight {
public static void main(String[] args) {
Person p1 = new Person("ur sister", "121");
Person p2 = new Person("ur brother", "121");
Person p3 = new Person("ur mon", "122");
System.out.println("p1 equals p2?" + p1.equals(p2));
System.out.println("p2 equals p3?" + p1.equals(p3));
}
}
equals() method accepts Object, not Person. Object class doesn't have getIdStr() method. In other words - you do really work with Person, but initially it is represented like Object, so you need to cast it to Person first.
You aren't "creat[ing] a new" personObj, but rather creating a variable named "personObj" that references the existing "obj" but is aware of its more specific runtime type "Person". This conversion is necessary, because the type Object does not have the functionality of Person (i.e. getIdStr() is a function that exists on Person, but not on Object), so to use it, you must know that the object you are dealing with has a type with a method matching that signature. The statement "Person personObj = (Person) obj" is doing what is called a "cast", which is validating that the variable "obj" is of type "Person" and providing you another view of this very same object but with this more specific type information available to it.

Collection with multiple values in key

I'm looking for a Collection type data structure to implement the following. Say I have a class like this:
class Person() {
String homeTown; // key
String sex; // key
String eyeColour; // key
String name;
long height;
// other stuff....
}
I am processing multiple Person objects. I want to organise them into sets whereby each set contains Person objects with the same homeTown, sex and eyeColour. At the moment I am implementing something like this:
Map<String, HashSet<Person>> = new HashMap<String, HashSet<Person>>;
where the key is a concatanation of the homeTown, sex and eyeColour. This works but seems a bit untidy - can anyone suggest a more elegant solution or a better data structure to use, thanks?
You could restructure your class to make the key explicit. This is more robust than simply concatenating the key values and avoids any additional object creation overhead at the point when you wish to store your Person instance in a Map, because you've eagerly created the key in advance.
public class Person {
public class Key {
private final String homeTown;
private final String sex;
private final String eyeColour;
public Key(String homeTown, String sex, String eyeColour) { ... }
public boolean equals(Object o) { /* Override to perform deep equals. */ }
public int hashCode() { /* Could pre-compute in advance if the key elements never change. */ }
}
private final Key key;
private final String name;
private final long height;
public Person(String homeTown, String sex, String eyeColour, String name, long height) {
this.key = new Key(homeTown, sex, eyeColour);
this.name = name;
this.height = height;
}
public Key getKey() {
return key;
}
public String getName() { return name; }
public long getHeight() { return height; }
}
Create an object to model your key. For example class PersonKey { String homeTown, sex, eyeColour } (getters and setters omitted for brevity)
Implement the equals and hashCode method for this object.
Use this object as the key in your Map.
Either remove the attributes from your Person object or replace them with a reference to your PersonKey object.
In addition, consider making the type of your map the following i.e. you don't need to specify what type of Set you are using as the key to your map.
Map<String, Set<Person>> = new HashMap<String, Set<Person>>();
And, if you are using a Set<Person> then you'll need to override equals and hashCode for the Person as well, otherwise the Set cannot correctly determine if two Person objects represent the same person or not, which is needed to make sure the collection contains only unique elements.
org.apache.commons.collections.map.MultiValueMap
You can use guava's Sets.filter method to filter the person objects.
Example:
Person class:
public class Person {
String name;
String hometown;
int age;
public Person(String name, String hometown, int age) {
this.name = name;
this.age = age;
this.hometown = hometown;
}
#Override
public int hashCode() {
int hash = 17;
hash = 37 * hash + name.hashCode();
hash = 37 * hash + hometown.hashCode();
hash = 37 * hash + age;
return hash;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
Person p;
if (obj instanceof Person)
p = (Person) obj;
else
return false;
if (this.name.equals(p.name) && this.hometown.equals(p.hometown)
&& this.age == p.age)
return true;
return false;
}
#Override
public String toString() {
StringBuilder b = new StringBuilder();
b.append("[name = ").append(name).append("\n");
b.append("home town = ").append(hometown).append("\n");
b.append("age = ").append(age).append("]");
return b.toString();
}
}
TestGuavaFilter class:
public class TestGuavaFilter {
public static void main(String[] args) {
Set<Person> set = new HashSet<Person>();
set.add(new Person("emil", "NY", 24));
set.add(new Person("Sam", "NY", 50));
set.add(new Person("george", "LA", 90));
System.out.println(Sets.filter(set, new FilterHomeTown("NY")));
}
}
class FilterHomeTown implements Predicate<Person> {
String home;
public FilterHomeTown(String home) {
this.home = home;
}
#Override
public boolean apply(Person arg0) {
if (arg0.hometown.equals(this.home))
return true;
return false;
}
}
The advantage of using a filter is that you can filter Person object in any way ,suppose you want to filter only using home-town and not the other 2 attributes this will helpful.More over since guava's filter only produces a view of the the real Set,you can save memory.

Categories