Reputation: 315
I'm currently developing an Android application with an activity containing a RecyclerView with aprox. 1k items (database viewer). The user has to be able to search through these items, so I implemented a search with this filter:
class PersonFilter extends Filter
{
private PersonViewAdapter adapter;
private Person[] originalPeople;
public PersonFilter(PersonViewAdapter adapter, Person[] unfiltered)
{
super();
this.adapter = adapter;
this.originalPeople = unfiltered;
}
@Override
protected FilterResults performFiltering(CharSequence constraint)
{
constraint = constraint.toString().toLowerCase();
FilterResults results = new FilterResults();
if (constraint.length() == 0)
{
results.values = originalPeople;
results.count = originalPeople.length;
}
else
{
ArrayList<Person> filtered = new ArrayList<>();
for (Person p : originalPeople)
{
//region filtering
if (p instanceof Student)
{
Student s = (Student)p;
if (s.fullName.toLowerCase().contains(constraint) ||
s.mngClass.name.contains(constraint) ||
s.getShortUsername().contains(constraint) ||
s.getLongUsername().contains(constraint))
{
filtered.add(p);
}
}
else if (p instanceof Teacher)
{
Teacher t = (Teacher)p;
if (t.fullName.toLowerCase().contains(constraint) ||
t.acronym.toLowerCase().contains(constraint) ||
t.getShortUsername().contains(constraint) ||
t.getLongUsername().contains(constraint))
{
filtered.add(p);
}
}
else
{
if (p.fullName.toLowerCase().contains(constraint) ||
p.getShortUsername().contains(constraint) ||
p.getLongUsername().contains(constraint))
{
filtered.add(p);
}
}
//endregion
}
Person[] dummy = new Person[filtered.size()];
results.values = filtered.toArray(dummy);
results.count = filtered.size();
}
return results;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results)
{
adapter.setPeople((Person[])results.values);
adapter.notifyDataSetChanged();
}
}
The problem is, that this way the search is not performing well and as I am not very experienced in Java/Android performance optimization, I wondered what I could improve here.
Here's the 'Person' class (if needed):
public abstract class Person
{
public int id;
public String fullName;
public String firstName;
public String lastName;
public String getShortUsername()
{
String ln = toUsernameForm(lastName);
String username = ln.substring(0, Math.min(ln.length(), 7)) + firstName.substring(0, 1).toLowerCase();
return username;
}
public String getLongUsername()
{
return toUsernameForm(firstName) + "." + toUsernameForm(lastName);
}
public static String toUsernameForm(String s)
{
return normalize(s.split("\\W")[0].toLowerCase().replace(" ", "")
.replace("ä", "ae")
.replace("ö", "oe")
.replace("ü", "ue")
.replace("ß", "ss"));
}
public static String normalize(String s)
{
String nfdNormalizedString = Normalizer.normalize(s, Normalizer.Form.NFD);
Pattern pattern = Pattern.compile("\\p{InCombiningDiacriticalMarks}+");
return pattern.matcher(nfdNormalizedString).replaceAll("");
}
}
Upvotes: 0
Views: 209
Reputation: 907
This version should reduce type casting.
ArrayList<Person> filtered = new ArrayList<>();
for (Person p : originalPeople)
{
//region filtering
if (p.fullName.toLowerCase().contains(constraint) ||
p.getShortUsername().contains(constraint) ||
p.getLongUsername().contains(constraint))
{
filtered.add(p);
}
else if (p instanceof Student)
{
Student s = (Student) p;
if (s.mngClass.name.contains(constraint))
{
filtered.add(p);
}
}
else if (p instanceof Teacher)
{
Teacher t = (Teacher) p;
if (t.acronym.toLowerCase().contains(constraint))
{
filtered.add(p);
}
}
//endregion
}
results.values = filtered.toArray();
results.count = filtered.size();
It also seems that if long user name contains a character sequence, the short version will do too (maybe with the exception of special characters). You may leverage this fact to reduce comparisons.
Upvotes: 1