I've got an Object in Java representing the contents of a database, like so:
public Database {
int varA;
String varB;
double varC;
}
Now I'm trying to select and order certain elements for forther processing, but I want to make it configurable, so I created an enum which represents all attributes of the object like
public enum Contents {
VarA,
VarB,
VarC;
}
So now when I create a selection like
Contents[] select = { Contents.VarC, Contents.VarB };
i want to generate a List of String values representing the actual database contents from this. Now the only Implementation i could think of is switching for each entry in the selection, with has a pretty ugly quadratic complexity...
public List<String> switchIT(Database db, Contents[] select) {
List<String> results = new ArrayList<String>();
for (Contents s : select) {
switch(s) {
case VarA:
results.add(db.varA.toString());
break;
//go on...
}
}
return results;
}
is there a more direct way to map between enum and dynamic object values?
Or in more general terms: What is the best way to select values from an object dynamically?
Use the power of Java enums, which are fully-fledged classes.
public enum Contents {
VarA { public String get(Database d) { return d.getVarA(); } },
VarB { public String get(Database d) { return d.getVarB(); } },
VarC { public String get(Database d) { return d.getVarC(); } };
public String get(Database d) { return ""; }
}
Your client code then becomes
public List<String> switchIT(Database db, Contents[] select) {
List<String> results = new ArrayList<String>();
for (Contents s : select) results.add(s.get(db));
return results;
}
A more concise, but slower, solution would be to use a single implementation of get based on reflection and use the name of the enum member to generate the appropriate getter name:
public enum Contents {
VarA, VarB, VarC;
private final Method getter;
private Contents() {
try {
this.getter = Database.class.getMethod("get"+name());
} catch (Exception e) { throw new RuntimeException(e); }
}
public String get(Database d) {
try {
return (String) getter.invoke(d);
} catch (Exception e) { throw new RuntimeException(e); }
}
}
Related
I have two objects(which contain of list of objects) of same class.I need to find whether both are same or not.
Consider below example:
class Device {
String deviceName;
String devLocation;
String devType;
String devID;
public String getDeviceName() {
return deviceName;
}
public void setDeviceName(String deviceName) {
this.deviceName = deviceName;
}
public String getDevLocation() {
return devLocation;
}
public void setDevLocation(String devLocation) {
this.devLocation = devLocation;
}
public String getDevType() {
return devType;
}
public void setDevType(String devType) {
this.devType = devType;
}
public String getDevId() {
return devID;
}
public void setDevId(String devId) {
this.devID = devId;
}
}
class DevList {
List<Device> deviceList;
public List<Device> getDevices() {
return deviceList;
}
public void setDevices(List<Device> deviceList) {
this.deviceList = deviceList;
}
}
Need to compare two objects of DevList class.
Will get a new DevList object for every regular interval of time.
Every time i need to verify current object with previous object and update DB if there is any difference else ignore.
The list(deviceList) in current and previous objects might not be in same order.
for example:
Consider below two objects which are in Json format(Please ignore json format errors).
Object 1:
{
"devList":[
{
"deviceName":"ABC",
"devLocation":"India",
"devType":"Router",
"devID":"1111"
},
{
"deviceName":"XYZ",
"devLocation":"India",
"devType":"Router",
"devID":"2222"
}
]
}
Object 2:
{
"devList":[
{
"deviceName":"XYZ",
"devLocation":"India",
"devType":"Router",
"devID":"2222"
},
{
"deviceName":"ABC",
"devLocation":"India",
"devType":"Router",
"devID":"1111"
}
]
}
We can do it by iterating lists in both objects by checking devID. But complexity would be M*N.
Is there any other way?
You could override equals in Device to compare each Device object.
Something similar
class Device {
...
...
//your getter and setter
public boolean equals (Object obj)
{
if (Device.class != obj.getClass()) {
return false;
}
if (obj instanceof Device) {
return (deviceName.equals(((Device) obj).deviceName) &&
devLocation.equals(((Device) obj).devLocation) &&
devType.equals(((Device) obj).devType) &&
devID.equals(((Device) obj).devID));
}
return false;
}
}
Now, call removeAll, which will remove same object list from List1
List1.equals(List2);
So, if both list has same object list, then List1 would be empty.
Step 1: First override equal method in Device Class
Step 2: override the equal() in your DevList class with
with below logic
1. use one Set collection
2. add object into it from 1st Object's list
3. check size() of set
3. add object from 2nd Object's list
5. check size() of set every time you add from 2nd list
6. if more than the size of one list it is not equal(As Set will not keep duplicate value)
I have two classes as shown below. I need to use these two classes to extract few things.
public final class ProcessMetadata {
private final String clientId;
private final String deviceId;
// .. lot of other fields here
// getters here
}
public final class ProcMetadata {
private final String deviceId;
private final Schema schema;
// .. lot of other fields here
}
Now I have below code where I am iterating above two classes and extracting schema given a clientId.
public Optional<Schema> getSchema(final String clientId) {
for (ProcessMetadata metadata1 : processMetadataList) {
if (metadata1.getClientId().equalsIgnoreCase(clientId)) {
String deviceId = metadata1.getDeviceId();
for (ProcMetadata metadata2 : procMetadataList) {
if (metadata2.getDeviceId().equalsIgnoreCase(deviceId)) {
return Optional.of(metadata2.getSchema());
}
}
}
}
return Optional.absent();
}
Is there any better way of getting what I need by iterating those two above classes in couple of lines instead of what I have? I am using Java 7.
You're doing a quadratic* search operation, which is inneficient. You can do this operation in constant time by first creating (in linear time) a mapping from id->object for each list. This would look something like this:
// do this once, in the constructor or wherever you create these lists
// even better discard the lists and use the mappings everywhere
Map<String, ProcessMetadata> processMetadataByClientId = new HashMap<>();
for (ProcessMetadata process : processMetadataList) {
processMetadataByClientId.put(process.getClientId(), process);
}
Map<String, ProcMetadata> procMetadataByDeviceId = new HashMap<>();
for (ProcMetadata metadata2 : procMetadataList) {
procMetadataByDeviceId.put(proc.getDeviceId(), proc);
}
Then your lookup simply becomes:
public Optional<Schema> getSchema(String clientId) {
ProcessMetadata process = processMetadataByClientId.get(clientId);
if (process != null) {
ProcMetadata proc = procMetadataByDeviceId.get(process.getDeviceId());
if (proc != null) {
return Optional.of(proc.getSchema());
}
}
return Optional.absent();
}
In Java 8 you could write it like this:
public Optional<Schema> getSchema(String clientId) {
return Optional.fromNullable(processMetadataByClientId.get(clientId))
.map(p -> procMetadataByDeviceId.get(p.getDeviceId()))
.map(p -> p.getSchema());
}
* In practice your algorithm is linear assuming client IDs are unique, but it's still technically O(n^2) because you potentially touch every element of the proc list for every element of the process list. A slight tweak to your algorithm can guarentee linear time (again assuming unique IDs):
public Optional<Schema> getSchema(final String clientId) {
for (ProcessMetadata metadata1 : processMetadataList) {
if (metadata1.getClientId().equalsIgnoreCase(clientId)) {
String deviceId = metadata1.getDeviceId();
for (ProcMetadata metadata2 : procMetadataList) {
if (metadata2.getDeviceId().equalsIgnoreCase(deviceId)) {
return Optional.of(metadata2.getSchema());
}
}
// adding a break here ensures the search doesn't become quadratic
break;
}
}
return Optional.absent();
}
Though of course using maps ensures constant-time, which is far better.
I wondered what could be done with Guava, and accidentally wrote this hot mess.
import static com.google.common.collect.Iterables.tryFind
public Optional<Schema> getSchema(final String clientId) {
Optional<String> deviceId = findDeviceIdByClientId(clientId);
return deviceId.isPresent() ? findSchemaByDeviceId(deviceId.get()) : Optional.absent();
}
public Optional<String> findDeviceIdByClientId(String clientId) {
return tryFind(processMetadataList, new ClientIdPredicate(clientId))
.transform(new Function<ProcessMetadata, String>() {
String apply(ProcessMetadata processMetadata) {
return processMetadata.getDeviceId();
}
});
}
public Optional<Schema> findSchemaByDeviceId(String deviceId) {
return tryFind(procMetadataList, new DeviceIdPredicate(deviceId.get())
.transform(new Function<ProcMetadata, Schema>() {
Schema apply(ProcMetadata procMetadata) {
return processMetadata.getSchema();
}
});
}
class DeviceIdPredicate implements Predicate<ProcMetadata> {
private String deviceId;
public DeviceIdPredicate(String deviceId) {
this.deviceId = deviceId;
}
#Override
public boolean apply(ProcMetadata metadata2) {
return metadata2.getDeviceId().equalsIgnoreCase(deviceId)
}
}
class ClientIdPredicate implements Predicate<ProcessMetadata> {
private String clientId;
public ClientIdPredicate(String clientId) {
this.clientId = clientId;
}
#Override
public boolean apply(ProcessMetadata metadata1) {
return metadata1.getClientId().equalsIgnoreCase(clientId);
}
}
Sorry.
I'm writing a messaging system to queue actions for my program to execute. I need to be able to pass various objects by the messages. I currently have a Msg object that accepts (Action enum, Data<?>...object). The Data object is intended to be a wrapper for any object I might pass.
Currently the Data object uses this code, with generics:
public class Data<T> {
private T data;
public Data(T data){
this.data = data;
}
public T getData(){
return data;
}
}
The Msg object takes Data<?>... type, so Msg has a Data<?>[] field.
If getData() is called on a Data<?> object, it returns the Object type. Obviously not ideal.
I need to be able to pass, say, Image objects as well as String objects. I'm certain there's a better way of passing arbitrary data.
The reason you're having trouble is that you're trying to get the static typing system of Java to do something that it can't. Once you convert from a Data<T> to a Data<?>, whatever T was is effectively lost. There's no clean way to get it back.
The quickest way to get it to work (from what you have right now) is to start throwing casts everywhere, like this:
Data<?> d = new Data("Hello");
String contents = (String)d.getData();
This is kind of a terrible idea, so let's go back to the drawing board.
If (ideally), you have all of the types you could ever need ahead of time (i.e. every Data is either a String or an Image or an Integer), then you can pretty easily (though it's a bit tedious) define a Sum type (aka a union if you're coming from C) of the different types of data you'll have to handle. As a class invariant, we assume that exactly one of the fields is non-null, and the rest are null. For this example I'll assume it can be either a String, an Image, or an Integer, but it's fairly simple to add or remove types from Data as necessary.
public class Data {
private Image imgData;
private String stringData;
private Integer intData;
public Data(Image img) {
this.imgData = img;
}
public Data(String stringData) {
this.stringData = stringData;
}
public Data(Integer intData) {
this.intData = intData;
}
public boolean isImage() {
return imageData != null;
}
public boolean isInteger() {
return intData != null;
}
public boolean isString() {
return stringData != null;
}
public Image asImage() {
if(! isImage()) throw new RuntimeException();
return imgData;
}
public Image asString() {
if(! isString()) throw new RuntimeException();
return stringData;
}
public Image asInt() {
if(! isInt()) throw new RuntimeException();
return intData;
}
}
One necessary side effect is that we cannot wrap null without causing exceptional behavior. Is this is desired, it isn't too difficult to modify the class to allow for it.
With this Data class, it's pretty easy to do if-else logic to parse it.
Data d = ....... //Get a data from somewhere
if(d.isImage()) {
Image img = d.asImage();
//...
} else if (d.isString()) {
String string = d.asString();
//...
} else if (d.isInteger()) {
Integer i = d.asInt();
//...
} else {
throw new RuntimeException("Illegal data " + d + " received");
}
If you call getData().getClass() you will get the class or type that was passed, which doesn't seem to me to be the same as an Object. You might not know what you are getting, but you can either find out or define a common interface for everything you might pass. You could for example, call toString() or getClass() on anything passed. Your question is that you are passing any conceivable object, so my question is what are you going to do with it? If you are going to serialize it into a database you don't need know anything about what type it is, otherwise you can test it or call a common interface.
public class PlayData {
class Msg {
private List<Data<?>> message = new ArrayList<Data<?>>();
public void addData(Data<?> datum) { message.add(datum); }
public void printTypes() { for ( Data<?> datum: message ) { System.out.println(datum.getData().getClass()); } }
}
class Data<T> {
private T value;
public Data(T value) { this.value = value; }
public T getData() { return value; }
}
class Listener {
public void receive(Msg msg) { msg.printTypes(); }
}
class Sender {
private Listener listener;
public Sender(Listener listener) { this.listener = listener; }
public void send(Msg msg) { listener.receive(msg); }
}
class MyPacket {
int i;
public MyPacket(int i) { this.i = i; }
}
public static void main(String[] args) throws Exception { new PlayData().run(); }
public void run() throws Exception {
Sender sender = new Sender(new Listener());
Msg msg = new Msg();
msg.addData(new Data<String>("testing") );
msg.addData(new Data<MyPacket>(new MyPacket(42)) );
sender.send(msg);
}
}
context :
i'm working on an applet witch manage fidelity cards. past version of the app had been done by an other developper. there is no documentation. i have to improve it.
problem :
to reach a customer, the app have some combobox. those combobox are filled by vertors
i try to sort item in the combobox but fail each times
i've read things about Collections.sort(x); where x could be a List or a Vector
but wherever i put the instruction for sorting elements, eclipse mark sort with this error :
Bound mismatch: The generic method sort(List<T>) of type Collections is not applicable for the arguments (Vector<NomClient>). The inferred type NomClient is not a valid substitute for the bounded parameter <T extends Comparable<? super T>>
here is the code of the combobox :
private JComboBox<Object> getComboBox() {
if (this.comboBox == null) {
this.comboBox = new JComboBox<Object>();
this.comboBox.addActionListener(new ActionListener() {
#Override
public void actionPerformed(final ActionEvent e) {
try {
SelectionNumeroCarteFidelite2.this.name = SelectionNumeroCarteFidelite2.this.comboBox
.getSelectedItem().toString();
SelectionNumeroCarteFidelite2.this.mod2 = new DefaultComboBoxModel<Object>(
Select.listePrenomclientfidelite(SelectionNumeroCarteFidelite2.this.name));
SelectionNumeroCarteFidelite2.this.comboBox_1
.setModel(SelectionNumeroCarteFidelite2.this.mod2);
SelectionNumeroCarteFidelite2.this.lblTaperOuSlectionner
.setVisible(false);
} catch (final Exception e1) {
final String message = "Choix Impossible - Merci de vérifier votre sélection";
System.out.print("Nom " + message);
final AlerteSelection fenetre = new AlerteSelection(
SelectionNumeroCarteFidelite2.this.interfaceactuelle,
message);
fenetre.setVisible(true);
SelectionNumeroCarteFidelite2.this.interfaceactuelle
.setEnabled(false);
SelectionNumeroCarteFidelite2.this.lblValider
.setVisible(false);
SelectionNumeroCarteFidelite2.this.lblTaperOuSlectionner
.setVisible(true);
}
}
});
this.comboBox.setEnabled(false);
this.comboBox.setForeground(Color.GRAY);
this.comboBox.setFont(new Font("Tahoma", Font.BOLD, 11));
this.comboBox.setEditable(true);
this.comboBox.setBorder(null);
this.comboBox.setBackground(Color.WHITE);
this.comboBox.setBounds(528, 426, 278, 22);
this.mod = new DefaultComboBoxModel<Object>(
Select.listenomclientfidelite());
this.comboBox.setModel(this.mod);
AutoCompletion.enable(this.comboBox);
}
return this.comboBox;
}
here is the code of the Select.listenomclientfidelite()
public static Object[] listenomclientfidelite() {
final Vector<NomClient> requete = new Vector<NomClient>();
try {
c = Connexion.getCon();
final String sql = "SELECT DISTINCT NOMCLIENT FROM CARTE_DE_FIDELITE INNER JOIN CLIENT ON CLIENT.IDCLIENT=CARTE_DE_FIDELITE.IDCLIENT";
preStm = c.prepareStatement(sql);
rs = preStm.executeQuery();
} catch (final Exception e) {
System.out.print("erreur" + e.getMessage());
}
try {
requete.add(null);
NomClient liste;
while (rs.next()) {
liste = new NomClient();
liste.setNom(rs.getString(1));
requete.add(liste);
System.out.println("listenomclientfidelite, liste is : "+liste);
}
rs.close();
preStm.close();
} catch (final Exception e) {
System.out.print("errorlistenom" + e.getMessage());
}
return requete.toArray(new Object[0]);
After beeing advised by Hovercraft Full Of Eels to modify my class NomClient, i understood that my NomCli class was the problen and not the use of vector, so here is a new step but no solution yet , so here is my Modified NomClient Class :
public class NomClient implements Comparable<NomClient> {
String nom;
public String getNom() {
return this.nom;
}
public void setNom(final String nom) {
this.nom = nom;
}
#Override
public String toString() {
return this.nom;
}
#Override
public int compareTo(NomClient other) {
System.out.println("nom : "+this.nom);
System.out.println("nom to string : "+this.nom.toString());
System.out.println(other.nom);
System.out.println("compare to : "+other.nom.toString());
int last = this.nom.toString().compareTo(other.nom.toString());
return last == 0 ? this.nom.compareTo(other.nom) : last;
}
}
i also have added Collection sort just before the return statement in sselect.listenomclientfidelite(),
like this :
Collections.sort(requete);
return requete.toArray(new Object[0]);
Now i have to deal with a java.lang.NullPointerException. "other" is null
Does any one have a clue to properly sort my combobox ?
If you can't change the NomClient class so that it implements Comparable<NomClient>, meaning that you'd have to give it a public int compareTo(NomClient o) method, then use a Comparator<NomClient> in your sort method call. This is a class that you create that has one method, public int compare(NomClient o1, NomClient o2), and that returns a -1, 0, or 1, depending on if o1 is functionally less than, equal to or greater than the o2 parameter. You would pass your Comparator instance as the 2nd paramter in your Collections.sort(myCollection, myComparator) method call.
Note that your problem has nothing to do with use of a Vector, and all to do with the NomClient class not implementing Comparable.
thank to Hovercraft Full Of Eels his solution was the good one.
i realised the first item of my vector was null it's why the comparable methode failed. so i worked around to handle this case, and here are the final implementation :
for NomClient.java :
public class NomClient implements Comparable<NomClient> {
String nom;
public String getNom() {
return this.nom;
}
public void setNom(final String nom) {
this.nom = nom;
}
#Override
public String toString() {
return this.nom;
}
#Override
public int compareTo(NomClient other) {
// compareTo should return < 0 if this is supposed to be
// less than other, > 0 if this is supposed to be greater than
// other and 0 if they are supposed to be equal
int last = 10;
if (other != null){
last = -10;
if (this.nom != null){
last = this.nom.compareTo(other.nom);
}
}
return last;
for Select.listenomclientfidelite()
public static Object[] listenomclientfidelite() {
final Vector<NomClient> requete = new Vector<NomClient>();
try {
c = Connexion.getCon();
final String sql = "SELECT DISTINCT NOMCLIENT FROM CARTE_DE_FIDELITE INNER JOIN CLIENT ON CLIENT.IDCLIENT=CARTE_DE_FIDELITE.IDCLIENT";
preStm = c.prepareStatement(sql);
rs = preStm.executeQuery();
} catch (final Exception e) {
System.out.print("erreur" + e.getMessage());
}
try {
requete.add(null);
NomClient liste;
while (rs.next()) {
liste = new NomClient();
liste.setNom(rs.getString(1));
requete.add(liste);
System.out.println("listenomclientfidelite, liste is : "+liste);
}
rs.close();
preStm.close();
} catch (final Exception e) {
System.out.print("errorlistenom" + e.getMessage());
}
Collections.sort(requete);
return requete.toArray(new Object[0]);
}
nothing else to do than insert the returned ArrayList in a combobox.
I have a Java Enum:
public enum CodeType {
BRONZE("00001BP", "BAP"),
SILVER("00002SL", "SAP"),
GOLD("00003GL", "GAP"),
MOBILE("00004MB", "TCM"),
SOCIAL("00005SM", "ASM"),
WEB_PRESENCE("00006WP", "GLO"),
EMAIL_MARKETING("00007EM", "PEM"),
CUSTOM_DIAMOND("00008CD", "PCS"),
CONSUMER_PORTAL("00009CP", "CPS");
private String code;
private String key;
CodeType(String code, String key) {
this.code = code;
this.key = key;
}
...
}
As you see, I have nine elements and each has two values. My question is How can I load values for those elements from a file like properties or xml? I mean:
BRONZE(isLoadedFromFile, isLoadedFromFile),
...
CONSUMER_PORTAL(isLoadedFromFile, isLoadedFromFile);
Thanks so much.
Try something like this..
public enum EnumTest {
BRONZE, SILVER;
public String getProperty(String keyOrCode) {
Properties prop = new Properties();
try {
prop.load(new FileInputStream("E:\\EnumMapper.properties"));
} catch (Exception e) {
e.printStackTrace();
}
return prop.getProperty(this.name() + "." + keyOrCode);
}
public String getCode() {
return getProperty("CODE");
}
public String getKey() {
return getProperty("KEY");
}
public static void main(String[] args) {
System.out.println(EnumTest.BRONZE.getCode());
System.out.println(EnumTest.BRONZE.getKey());
}
}
where the EnumMapper.properties contains
BRONZE.CODE=00001BP
BRONZE.KEY=BAP
SILVER.CODE=00002SL
SILVER.KEY=SAP
Just wanted to share some possibilities..
If I understand your question correctly, you would need to do so in the constructor (which is misnamed in your example).
The hard-coded defaults you show would serve as the defaults, but in the constructor you would check/load some properties file and override them.
In general though, this smells of an odd/bad design. You would need to hard-code that properties file / resource in the enum. You're also dynamically loading what is meant to be something that represents a constant value.
It seems like really you should be using your own class to hold these values.
One option is to generate a static map based on the resource file within the enum class, mapping from enum values to the data in the file. The map can then be used for the getter.
For instance with a resource file formatted like this:
A=red
B=blue
C=yellow
it can be initialized like this:
public enum MyEnum {
A, B, C;
public String getFoo() {
return enumFooValuesFromResourceFile.get(this);
}
private static final Map<MyEnum, String> enumFooValuesFromResourceFile;
static {
Map<MyEnum, String> temp = Collections.emptyMap();
try {
String data = new String(MyEnum.class.getResourceAsStream("resourcepath").readAllBytes());
temp = Arrays.stream(data.split("\n"))
.map(line -> line.split("="))
.collect(Collectors.<String[], MyEnum, String>toMap(
key_val -> MyEnum.valueOf(key_val[0]),
key_val -> key_val[1]));
} catch (IOException iE) {
// helpful message.
} finally { enumFooValuesFromResourceFile = temp; }
}
}
A nicer option, I think, is to use a static String for the resource file data, and store the values directly on the enum items during initialization. During enum initialization, you cannot access a static property of the enum, so it must either be outside it, or in an inner class using the Initialization-on-demand holder idiom (credit to) which is neat, because it's lazy and not loaded if the enum is never accessed.
(I found I can set the (non-final) String to null at the end of the enum declaration, freeing that memory.)
public enum MyEnum {
A, B, C;
public String getFoo() { return foo; }
final String foo;
MyEnum() {
foo = getFooValue();
}
private String getFooValue() {
return Arrays.stream(ResourceHolder.resourceFileString.split("\n"))
.filter(str -> str.startsWith(this.name() + '='))
.findFirst()
.map(str -> str.replaceAll("^" + this.name() + '=', ""))
.orElseThrow(() ->
new IllegalArgumentException(this.name() + " not found in resourcefile."));
}
// Release resources (string) from memory after enum initialization.
static {ResourceHolder.resourceFileString = null;}
private static class ResourceHolder {
// Lazily initialized if/when MyEnum is accessed.
// Cleared after initialization.
private static String resourceFileString;
static {
try {
InputStream lResource =
Objects.requireNonNull(MyEnum.class.getResourceAsStream("resourcepath"));
resourceFileString = new String(lResource.readAllBytes());
} catch (IOException iE) {
// helpful message.
iE.printStackTrace();
}
}
}
}