Should I use an EnumSet? - java

I am trying to build a "flat file reader." These files will have several related columns, such as "Customer Name", "Telephone," etc, but the column number will be different for each flat file; also, some flat files will have columns that others don't.
I want each of these different files to be readable by the FlatFileReader class. So I created an enum for the first flat file, where the value is an index for an array.
enum Columns { NAME, TELEPHONE, PAYMENT }
...
String[] columns = new String[3];
columns[0] = line.substring(0,29); //Name
columns[1] = line.substring(30,36); //Telephone
columns[2] = line.substring(37); //Payment
So in order to get, for example, a name from the flat file, FlatFileReader would call the method:
file.get (Columns.NAME);
and FlatFileA would look for index 0. There's going to be a wide variety of flat file classes, and I would like for each to inherit the same enum so that it's consistent; the issue is that some files won't have certain columns. So I can't do NAME, TELEPHONE, PAYMENT in the parent class (or interface) if FlatFileB doesn't have a TELEPHONE column.
How could I solve this problem? Is this how an EnumSet is used? Could each child class create an EnumSet and only add the constants it needs? If I created an EnumSet and only added NAME and PAYMENT, would PAYMENT now have a value of 1 (inside the EnumSet) or would it still have the value of 2?

EnumSet is just another implementation of the Set interface. Implementing the Set interface means it behaves just as much as a Set as any other implementation. The difference is in performance, accepted values and iteration order.
The benefit of the EnumSet is speed, the downside is that EnumSets can only have enum constants as members. Since each enum constant has a zero-based ordinal(), the EnumSet uses a bitstring representation, where each bit represents the presence/absence of an element in the set. Choosing EnumSet makes contains(All), add(All), remove(All) and retain(All) run much faster than for TreeSet and HashSet.
And no, an enum constant never changes its ordinal() (though I'm not sure whether that was what you meant).

I would suggest an ArrayList as your columns are in a sequence. I would further recommend you use an ArrayList of objects that encapsulate a Column along with details about how to gather the column from the records.
class Field {
Columns col;
int start;
int length;
}
ArrayList<Field> file1Record = new ArrayList<Field>();
// You could calculate the 0 and 29 on the fly.
file1Record.add(new Field(Columns.NAME, 0, 29));
...

Related

get a getter method from field name to avoid if-else

I have this code, which obviously doesn't look nice - it seems all the if-else can somehow be avoided.
if(sortBy.equals("firstName"))
personList.sort(Comparator.comparing(Person::getFirstName));
else if(sortBy.equals("lastName"))
personList.sort(Comparator.comparing(Person::getLastName));
else if(sortBy.equals("age"))
personList.sort(Comparator.comparing(Person::getAge));
else if(sortBy.equals("city"))
personList.sort(Comparator.comparing(Person::getCity));
else if(sortBy.equals("state"))
personList.sort(Comparator.comparing(Person::getState));
else if(sortBy.equals("zipCode"))
personList.sort(Comparator.comparing(Person::getZipCode));
the function takes sortBy, which is the name of one of the attributes of a Person, and applies a sorting to a personList based on that field. How can I avoid the if-else and write a better looking, possibily one line code?
Currently I have found that I can use a HashMap to create a mapping between a field name and a corresponding comparator.
map.put("age", Comparator.comparing(Person::getAge));
map.put("firstName", Comparator.comparing(Person::getFirstName))
...
And use personList.sort(map.get(sortBy)).
But still felt like it can further be improved without an extra step, to the point where it follows the open-closed principle, and adding a new field to Person would not need us to modify the code. I'm looking for something like
personList.sort(Comparator.comparing(Person::getterOfField(sortBy)))
UPDATE-1
For now, I decided to stick with using a Map<String, Function<Person, Comparable<?>> and I do not like to consider reflection based solutions. But still searching if I can find a similar way as this one where sort is a parameter.
UPDATE-2
I think a one-liner is not a good solution, cuz you wouldn't get a compile time error if one of the fields does not implement Comparator.
In general java doesn't want you to work with it this way1; it is not a structurally typed language, and unlike e.g. javascript or python, objects aren't "hashmaps of strings to thingies".
Also, your request more fundamentally doesn't add up: You can't just go from "field name" to "sort on that": What if the field's type isn't inherently sortable (is not a subtype of Comparator<Self>?)
What if there is a column in whatever view we're talking about / config file that is 'generated'? Imagine you have a field LocalDate birthDate; but you have a column 'birth month'2. You can sort on birth month, no problem. However, given that it's a 'generated value' (not backed directly by a field, instead, derived from a calculation based on field(s)), you can't just sort on this. You can't even sort on the backing field (as that would sort by birth year first, not what you want), nor does 'backing field' make sense; what if the virtual column is based on multiple fields?
It is certainly possible that currently you aren't imagining either virtual columns or fields whose type isn't self-sortable and that therefore you want to deposit a rule that for this class, you close the door on these two notions until a pretty major refactor, but it goes to show perhaps why "java does not work that way" is in fact somewhat 'good' (closely meshes with real life concerns), and why your example isn't as boilerplatey as you may have initially thought: No, it is not, in fact, inevitable. Specifically, you seem to want:
There is an exact 1-to-1 match between 'column sort keys' and field names.
The strategy to deliver on the request to sort on a given column sort key is always the same: Take the column sort key. Find the field (it has the same name); now find its getter. Create a comparator based on comparing get calls; this getter returns a type that has a natural sorting order guaranteed.
Which are 2 non-obvious preconditions that seem to have gotten a bit lost. At any rate, a statement like:
if(sortBy.equals("firstName"))
personList.sort(Comparator.comparing(Person::getFirstName));
encodes these 2 non-obvious properties, and trivially, therefore means it is also possible to add virtual columns as well as sort keys that work differently (for example, sorts on birth month, or, sorts on some explicit comparator you write for this purpose. Or even sorts case insensitively; strings by default do not do that, you'd have to sort by String.CASE_INSENSITIVE_COMPARATOR instead.
It strikes me as a rather badly written app if a change request comes in with: "Hey, could you make the sort option that sorts on patient name be case insensitive?" and you go: "Hooo boy that'll be a personweek+ of refactoring work!", no?
But, if you insist, you have 2 broad options:
Reflection
Reflection lets you write code that programatically gets a list of field names, method names, and can also be used to programatically call them. You can fetch a list of method names and filter out everything except:
instance methods
with no arguments
whose name starts with get
And do a simple-ish get-prefix-to-sort-key conversion (basically, .substring(3) to lop off the get, then lowercase the first character, though note that the rules for getter to field name get contradictory if the first 'word' of the field is a single letter, such as getXAxis, where half of the beanspec documents say the field name is definitely XAxis, as xAxis would have become getxAxis, and the other half say it is ambiguous and could mean the field name is XAxis or xAxis).
It looks something like this:
// intentionally raw type!
Map comparators = new HashMap();
for (Method m : Person.class.getMethods()) {
if (Modifiers.isStatic(m.getModifiers()) continue;
if (m.getParameterCount() != 0) continue;
String n = m.getName();
if (!n.startsWith("get") || n.length() < 4) continue;
n = Character.toLowerCase(n.charAt(3)) + n.substring(4);
comparators.put(n, (a, b) -> {
Object aa = m.invoke(a);
Object bb = m.invoke(b);
return ((Comparable) aa).compareTo(bb);
});
}
MyClass.COMPARATORS = (Map<String, Comparator<?>>) Collections.unmodifiableMap(comparators);
Note how this causes a boatload of errors because you just chucked type checking out the window - there is no actual way to ensure that any given getter type actually is an appropriate Comparable. The warnings are correct and you have to ignore them, no fixing that, if you go by this route.
You also get a ton of checked exceptions issues that you'll have to deal with by catching them and rethrowing something appropriate; possibly RuntimeException or similar if you want to disregard the need to deal with them by callers (some RuntimeException is appropriate if you consider any attempt to add a field of a type that isn't naturally comparable 'a bug').
Annotation Processors
This is a lot more complicated: You can stick annotations on a method, and then have an annotation processor that sees these and generates a source file that does what you want. This is more flexible and more 'compile time checked', in that you can e.g. check that things are of an appropriate type, or add support for mentioning a class in the annotation that is an implementation of Comparable<T>, T being compatible with the type of the field you so annotate. You can also annotate methods themselves (e.g. a public Month getBirthMonth() method). I suggest you search the web for an annotation processor tutorial, it'd be a bit much to stuff an example in an SO answer. Expect to spend a few days learning and writing it, it won't be trivial.
[1] This is a largely objective statement. Falsifiable elements: There are no field-based 'lambda accessors'; no foo::fieldName support. Java does not support structural typing and there is no way to refer to things in the language by name alone, only by fully qualified name (you can let the compiler infer things, but the compiler always translates what you write to a fully "named" (package name, type name that the thing you are referring to is in, and finally the name of the method or field) and then sticks that in the class file).
[2] At least in the Netherlands it is somewhat common to split patient populations up by birth month (as a convenient way to split a population into 12 roughly equally sized, mostly arbitrary chunks) e.g. for inviting them in for a checkup or a flu shot or whatnot.
Assuming that the sortBy values and the corresponding getters are known at compile, this would be a good place to use a string switch statement:
Function<Person.String> getter = null;
switch (sortBy) {
case "firstName":
getter = Person::getFirstName; break;
case "lastName":
getter = Person::getLastName; break;
...
}
personList.sort(Comparator.comparing(getter));
If you use a recent version of Java (Java 12 and later) you could use a switch expression rather than a switch statement.
Function<Person.String> getter;
getter = switch (sortBy) {
case "firstName" -> Person::getFirstName;
case "lastName" -> Person::getLastName;
...
default -> null;
}
personList.sort(Comparator.comparing(getter));
Note: you should do a better job (than my dodgy code) of dealing with the case where the sortBy value is not recognized.
As keshlam suggested, I think using the reflection API is the best fitting answer to your question, but keep in mind that using it in production code is generally discouraged.
Note: if you add a new Person-attribute which isn't itself Comparable, you'll have to resort to a custom Comparator anyway. With that in mind, you might want to keep the Map<String, Comparator<?>> solution you already have.

Indexing a simple Java Record

I have a Java Object, Record . It represents a single record as a result of SQL execution. Can CQEngine index collection of Record ?
My class is of the form
public class Record {
private List<String> columnNames;
private List<Object> values;
... Other getters
}
I have looked through some examples, but I have no luck there.
I want to index only specific column(s) with its name and corresponding value. Can this be achived using cqengine or is there any other alternatives to achieve the same.
Thanks.
That seems to be a strange way to model data, but you can use CQEngine with that model if you wish.
(First off, CQEngine will have no use for your column names so you can remove that field.)
To do this, you will need to define a CQEngine virtual attribute for each of the indexes in your list of values.
Each attribute will need to be declared with the data type which will be stored in that column/index, and will need to be able to cast the object at that index in your list of values, to the appropriate data type (String, Double, Integer etc.).
So let's say your Record has a column called 'price', which is of type Double, and is stored at index 5 in the list of values. You could define an attribute which reads it as follows:
public static final Attribute<Record, Double> PRICE =
attribute("PRICE", record -> ((Double) record.values.get(5));
If this sounds complicated, it's because that way of modelling data makes things a bit complicated :) It's usually easier to work with a data model which leverages the Java type system (which your model does not). As such, you will need to keep track of the data types etc. of each field programmatically yourself.
CQEngine itself will work fine with that model though, because at the end of the day CQEngine attributes don't need to read fields, the attributes are just functions which are programmed to fetch values.
There's a bunch of stuff not covered above. For example can your values be null? (if so, you should use the nullable variety of attributes as discussed in the CQEngine docs. Or, might each of your Record objects have different sets of columns? (if so, you can create attributes on-the-fly when you encounter a new column, but you should probably cache the attributes you have created somewhere).
Hope that helps,
Niall (CQEngine author)

put values of a list into a object

I have an object Person with the following fields:
firstName, secondName, age, nationallity, address, phoneNr.
And the list: ['John', 'Smith', '35', 'american', 'San Francisco', '+0324 235 327'].
I would like to put the values of the list into the object Person, without using the classic method of setting each value.
I want to avoid this:
person.setFirstName(list.get(0));
person.setSecondName(list.get(1));
person.setAge(list.get(2));
person.setNationallity(list.get(3));
person.setAddress(list.get(4));
person.setPhoneNumber(list.get(5));
My object has more fields than the ones I've put here as example (about 15), and I want to avoid writting a lot of code.
So my question is there a more elegant way of dumping all the values from the list into the object? I was thinking that in Java 8 maybe is there something but so far I haven't found anything.
There is no way to dump all values from the list into object, but you can add a new contructor in your Person Class, with a list as parameter like this :
public Person(List<String> list) {
this.firstName(list.get(0));
this.secondName(list.get(1));
this.age(list.get(2));
this.nationallity(list.get(3));
this.address(list.get(4));
this.phoneNumber(list.get(5));
}
and the call will be like :
Person person = new Person(list);
The other answer is good, there is one trick that can make it easier to work with though. This is especially relevant if there are a large number of entries such as the 15 mentioned in the question:
public Person(List<String> list) {
int index = 0;
this.firstName(index++);
this.secondName(index++);
this.age(index++);
this.nationallity(index++);
this.address(index++);
this.phoneNumber(index++);
}
Now you can add things to the list in any position or change the order or otherwise adjust it and don't have to manually update all the indexes. It also removes the risk of human error in accidentally getting an index wrong - although you do still need to get all the fields in the right order.

How to store multiple fields of different datatypes in any 2D array in java?

I want to store three values in a 2D type in java. I know that we can use List and ArrayList for storing 1D values but I need to store more than one field in a specific record. For example i have to enter the details for multiple columns i.e. (1,1),(1,2),(1,3) for details such aaaa, bbbb, cccc for a person and store them in one single row(which may consist of values which are other than string type). It should run in a loop and once details of a person is stored, it should store (2,1),(2,2),(2,3) i.e. again for a new person. How to do that?
And later on, how to retrieve and send the complete set to database together? Please help..
What you might want to do is to create a class that holds all of the information you want to keep related to a single record if it represents a concrete thing and use the List and ArrayList to store those.
What I mean by concrete thing is something that has a finite set of information that will stay the same over each object.
Something like:
public class Person
{
String name;
Integer age;
// etc...
}
This gives you two advantages over using something like a 2D array. First, it will make reading your code easier, since instead of having to remember that arrayName[x][0] is whatever you decide the first field is, you can access it using something like listItem.attributeName. The second advantage is that you can abstract out any common datahandling tasks as class methods instead of having to bloat your main class with it.

What would be better set, array or list?

I need to make a list of people and their time of arrival to a party, and when ever they leave I need to take them off this list. (the party maximum is 150)
Set would provide me that in no case I would add the same person twice.
List would provide me flexibility to start the list with few spaces (in case no one shows up).
Arrays (not sure what they provide) but I used them more often.
My idea was either to create 2 arrays one with names and what with times. When someone comes in, I save name in one and time on the other. When he/she leaves I search for his/her name, delete it and use the same index to delete the time on the other array.
A list could have one array of 2 elements, and then I will only need to add it in one location but searching would be a TINY more complicated.
Or maybe I am complicating this too much?
Map implementation:
public final class Person
{
... remainder left to the student ...
}
Map<Person, Date> currentPartyAttendees; // date is arrival time.
Set implementation:
public final class PartyAttendee
{
... person details ...
Date arrive;
int hashcode()
{
... use Apache HashCodeBuilder ...
}
boolean equals(Object other)
{
... implementation left to student. Use Apache EqualsBuilder ...
}
}
Set<PartyAttendee> currentPartyAttendees;
Using a HasMap would suit your purpose, as you can use the person's name as a key to add and retrieve the entry for the person, and it offers constant time performance, so regardless of how large the set grows, the performance should remain consistent.
The way you've described your use-case, why not consider the HashMap, or some other Map based implementation?
Unless of course, there's a binding for you to use a List [or similar] based data structure.
Just use a List<> and a Data structure the represents guest.
Subclass List to mark the arrival and departure time and add/remove methods. You can also use set, but then you'll have to generate a hashCode and equals method. I'm not sure you want to do that, cause people may have the same names (unless you have other data like SSN, bday, middle name etc)
public Class Guest{
private String firstName, lastName;
private long arrivalTime, departureTime;
....
}
public class MyGuests extends ArrayList<Guest>{
#Overide
public void add(Guest g){
//record arrival time here
super.add(g)
}
#Overide
public void remove(Guest g){
//record departure time here
super.remove(g);
}
}
I think you can use arrays as well, and, instead two arrays, use an arrays of 'Person' model, that holds the name of the person, arrive time and leave time. Before you insert on array, you can verify if the list already contains this person.
ps: don't forget to overwrite equals() and hashCode() in your model
LinkedHashMap - a container of key-value pairs that maintains the order of their insertion. The key would be the person (a simple String or a designated class), the value would be the time of arrival, e.g. a Date.

Categories

Resources