I need to have a relatively large number of categories defined (about 30 at start, we'll be adding more). Consider this code:
public class Category {
public static final Category DRESS = new Category("Dress");
public static final Category SKIRT = new Category("Skirt");
...
private static final List<Category> CATEGORIES = Arrays.aslist(DRESS, SKIRT, ...);
private String name;
public Category(String name) {
this.name = name;
}
//Some static public method to iterate over categories
...
I need to have the categories declared and also need a way to iterate over them. I discard reflection because I think it's not a very good practice.
Is declaring a large name of static final fields of the same class and also having them inside a list a good practice? As an alternative, I thought about having a Map<Integer, Category> instead the list, and the fields were integers that would identify each category, so you would get the categories by getting them inside the map. Would this be better in terms of time and space performance?
PS: It's for an android project, if it changes something
Consider this code:
public class Category {
public static final Category DRESS = new Category("Dress");
public static final Category SKIRT = new Category("Skirt");
Yeah this is literally what enums do in the background, so
public enum Category {
DRESS("Dress"),
SKIRT("Skirt"),
...;
private String name;
private Category(String name) {
this.name = name;
}
// Category.values() returns the elements as an array
You should use enum instead of creating an object with new Category("Dress"); because creating an object is expensive than using enum. Java enums are implemented more like classes, so you can change your code seamlessly:
public enum Category {
DRESS("Dress"), SKIRT("Skirt");
private final String name;
Category(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
Note:
The constructor for an enum type must be package-private or private access. It automatically creates the constants that are defined at the beginning of the enum body. You cannot invoke an enum constructor yourself.
Read more about enum at Enum Types
I would say using a List is good enough.
You should consider a Map only if you have to look up a
particular Category very frequently via some key property (like an int your case).
If There are no properties or methods in the Category class consider replace them with just Strings.
If new Categories are created at runtime and you want to persist them consider using a DB or File to save the Categories.
Edit: Answering the question in the comment
That would depend on the Category class. If its only purpose is to enumerate all the categories and the class itself does not have any other instance methods or properties then in terms of space complexity an Integer and your Category class is similar (since in a Map integer will be boxed in the Integer class object)
I would still suggest that you use a class called Category and a list if the purpose is only iterating over them and/or using specific instances of the Category class elsewhere in your application eg. Category.SOME_CATEGORY.
The following example is a good use-case
someMethod(Category category) {
// do something
}
versus
someMethod(int category) {
// before doing anything
// lookup this category by an int key
// in the the Map<Integer, Category>
}
The problem with the latter is that you could pass any int which may or may not be a valid key for a category. Using a class gives some bit for extra compile time check. Though you could always use an int def too. But again I would repeat that it all boils down to whether Category class has any instance methods or properties.
For small list, it is okay to use List or Map.
But for a large list, you may want to store them in a database.
Also ArrayList of String will be slightly efficient than using ArrayList of Category
Related
I've been trying to add Realm in my Android app. Their docs are pretty well explained & easy to follow. But it fails to explain this one particular area. I'm unable to figure out the practical use for the #Ignore annotation. I know that fields under this annotation are not persisted.
Can someone please share a few use cases. Also I wanted to know the scope of such fields. I mean, if I set an #Ignore field to some value, would that value be available to the other classes in my app for that particular launch session. If yes, then how do we access it? If no (which I guess is the case), then why do we need such a field anyway?
I've searched here and on web but couldn't find the relevant information. If out of my ignorance, I've missed upon some resource, please guide me to it.
Thanks.
Accordingly to the official documentation (see https://realm.io/docs/java/latest/) #Ignore is useful in two cases:
When you use GSON integration and your JSON contains more data than you want to store, but you still would like to parse it, and use right after.
You can't create custom getters and setter in classes extending RealmObject, since they are going to be overridden. But in case you want to have some custom logic anyway, ignored fields can be used as a hack to do that, because Realm doesn't override their getter & setters. Example:
package io.realm.entities;
import io.realm.RealmObject;
import io.realm.annotations.Ignore;
public class StringOnly extends RealmObject {
private String name;
#Ignore
private String kingName;
// custom setter
public void setKingName(String kingName) { setName("King " + kingName); }
// custom getter
public String getKingName() { return getName(); }
// setter and getter for 'name'
}
Ignored fields are accessible only from the object they were set in (same as with regular objects in Java).
UPDATE: As the #The-null-Pointer- pointed out in the comments the second point is out of date. Realm now allows having custom getters and setters in Realm models.
Here's a couple of real-world use cases:
1 - Get user's fullname:
public class User extends RealmObject {
private String first;
private String last;
#Ignore
private String fullName;
public String getFullName() {
return getFirst() + " " + getLast();
}
Get JSON representation of object:
public class User extends RealmObject {
private String first;
private String last;
#Ignore
private JSONObject Json;
public JSONObject getJson() {
try {
JSONObject dict = new JSONObject();
dict.put("first", getFirst());
dict.put("last", getLast());
return dict;
} catch (JSONException e) {
// log the exception
}
return null;
}
I've found it useful to define field names for when I am querying. For example
User.java
public class User extends RealmObject {
#Index
public String name;
#Ignore
public static final String NAME = "name";
}
And then later on I can do something like:
realm.where(User.class).equalTo(User.NAME, "John").findFirst();
This way if the schema changes from say name to id I don't have to hunt down every occurrence of "name".
Please see the the official documentation about #Ignore annotation:
The annotation #Ignore implies that a field should not be persisted to disk. Ignored fields are useful if your input contains more fields than your model, and you don’t wish to have many special cases for handling these unused data fields.
For this example I have an array:
String[] books = new String[x];
I would like to store the id and title in each location:
books[0]=>id:0, title:"book title1"
books[0]=>id:1, title:"book title2"
books[0]=>id:2, title:"book title3"
books[0]=>id:3, title:"book title4"
I want to store the id since it may change. I'm getting the id and title from a database. Getting the info isn't the issue. I want to store it this way so in my other functions this returns to I can use something like:
btn.setText(regions[i].title)
Any suggestion on how to handle this would be great.
Do one thing, first create a bean class like BookBean.
Under this declare two variables ID and Title. and declare getters and setters (If u are using eclipse u can easily do this by (Source -> generate getters and setters.. option)
and then declare a ArrayList to store BookBean vale as of follow.
ArrayList<BookBean> bookArrayList=new ArrayList<BookBean>();
for(int i=0;i<=urSize;i++)
{
// create a object for BookBean
BookBean book =new BookBean();
book.setID("what ever");
book.setTitle("what ever");
bookArrayList.ass(book)
}
It is better to use Arraylist with custom class.
see this
class Book
{
String id,title;
/* Cunstructor to store data */
public Book(String id,String title)
{
this.id = id;
this.title = title;
}
}
//declare arraylist
ArrayList<Book> bookList = new ArrayList<Book>();
bookList.add("1","book1");
bookList.add("2","book2");
bookList.add("3","book3");
bookList.add("4","book4");
btn.setText(bookList.get(i).title)
I think you have several options
Use a HashMap where you can use your id as key and value title
Define a class and keep id and title as attributes , define get and set methods.
Keep the objects of the class in a ArrayList
is there anyway i can have multi dimensional array ex: listarray string,int,int in android??
it is quite possible with Map & Set in Java/C++.
Make your own container with fields good explaining its purpose:
class PersonData {
public int age;
public int id;
public String name }
And make list of it:
List<PersonData> dataList = new ArrayList<PersonData>();
Acces your fields by:
dataList.get(5).age = 11;
As Egor said in comment, a good practice will be set those field as protected and create setters and getters, if you don't need extreme performance in this specific case.
I searched a solution but I didn't find it. I write an ArrayList containing my Name object to a file, and the retrieved ArrayList will later be used by different activities for the same purpose. This object is a separate class in a separate package.
I write the ArrayList with ObjectOutputStream and then close the FileOutpoutStream and ObjectOutputStream. I do it many times in an activity 's lifetime.
The object is like:
public class Name implements Serializable{
private static final long serialVersionUID = 7526472295622776147L;
String name;
int id;
public Name (String name, int id){
this.name = name;
this.id = id;
}
public String getName(){
return this.name;
}
public String getId(){
return this.id;
}
When I want to retrieve it, I create a new ArrayList of Name object and use ObjectInputStream to add elements by doing
ArrayList<Name> array = new ArrayList<Name>();
array.addAll((ArrayList<Name>)ois.readObject());
I avoided doing
array = (ArrayList<Name>) ois.readObject();
I thought there will be no reference this way.
I then close the FileInputStream and ObjectInputStream. I do this many times in an activity's lifecycle as well.
When I check MAT with eclipse, the histogram shows that memory contains many Name objects and thus, block the activities to destroy themself.
I tried to reset() Streams before closing them, I tried the opposite too, tried to only reset() whithout closing, I tried the same onDestroy(), but every actions lead to Exceptions and unwanted behaviors.
Changing the serial doesn't work to, it doesn't write or gives Exceptions so it seems that the serial must remain static and must not be removed.
How should I deal whith this, considering that this ArrayList must be stocked on the storage and shared with different activities? Should I use SharedPreferences instead or something else?
I'm sorting an array of custom objects (ListData[]) on two fields. I want it to be sorted by theme, and them by name. I thought i made a nice comparator in the custom object class and that i could use Arrays.sort(ld) to make my code working and sorting my array. But apparently im doing something wrong...
my custom object:
public class ListData implements Comparable<ListData>{
public int venueID;
public String name;
public String photoUrl;
public String tip;
public String theme;
#Override
public int compareTo(ListData ld0) {
return this.venueID- ld0.venueID;
}
public static Comparator<ListData> ListDataThemeAndNameComparator = new Comparator<ListData>() {
#Override
public int compare(ListData ld1, ListData ld2) {
String compareTheme1 = ld1.theme.toUpperCase();
String compareTheme2= ld2.theme.toUpperCase();
String compareName1 = ld1.name.toUpperCase();
String compareName2= ld2.name.toUpperCase();
//ascending
int comp = compareTheme1.compareTo(compareTheme2); // comp themes
if(comp==0){ // same theme
comp= compareName1.compareTo(compareName2); // compare names
}
return comp;
}
};
}
And in my main activity i have:
ListData ld[]= new ListData[jsonResponse.size()];
(some code filling my ListData array)
Arrays.sort(ld, ListData.ListDataThemeAndNameComparator); // compare by theme and then by name
Does anyone know what i'm doing wrong?
I edited my code But still it fails, now on a nullpointerexception on the compareTheme1 = ld1.theme.toUpperCase();. But i am sure my array is not empty, i logged it the line before sorting it and its filled with about 500 items.
Your ListData object should implements Comparable not Comparator interface.
EDIT:
To make things clear, you can sort an array by Array.sort(). To make custom sort, you can specify your comparator in Array.sort(), if you don't do that, array will be sorted in natural order which you can define by implementing Comparable interface. So you have two options how to custom sort:
by using custom comparator and specifying it in Array.sort()
by implementing Comparable interface to your items
I would suggest you to go with implementing Comparable. You save memory by not creating new comparator objects and Comparator is useful if you are comparing objects of different types which is not your case.