3

In Java is there something similar to C++ Standard Library lists that uses a comparator to find a specific object in a list by one of its variables?

For instance instead of looping through an ArrayList looking for a specific Object by checking for the variable comparison. Is there a way to use a comparator object to find a specific instance?

(Note: I don't want to use a hashmap as that would create two seperate lists. I want the functionality of a list without having to have a hashmap involved.)

Something like this, but for Java:

#include <algorithm>

using namespace std;

class Cperson     
{    
  string lastname, firstname, address, city;    
  int zipcode;    
  char state[3];    
  // this works for the last name    
  friend bool operator==(const Cperson& left, const Cperson& right);    
  friend bool firstEqualTo(const Cperson& left, const Cperson& right);    
};

bool operator==(const Cperson& left, const Cperson& right)    
{    
  return left.lastname == right.lastname;    
}

bool firstEqualTo(const Cperson& left, const Cperson& right)    
{    
  return left.firstname == right.firstname;    
}     

Now we can search our personlist on the firstname field, ignoring the other fields:

vector<Cperson> personlist;    
// fill personlist somehow

Cperson searchFor;   // should contain the firstname we want to find    
vector<Cperson>::iterator fperson;   
fperson= std::find(personlist.begin(),    
                   personlist.end(),   
                   searchFor,    
                   firstEqualTo);
andand
  • 17,134
  • 11
  • 53
  • 79
Lokiare
  • 1,238
  • 1
  • 15
  • 23
  • Or this: http://stackoverflow.com/questions/122105/java-what-is-the-best-way-to-filter-a-collection – duffymo Apr 23 '12 at 13:31
  • Are you specifically trying not to perform iteration? – Matt Mills Apr 23 '12 at 13:33
  • Yes, high level iteration is slower than a pre-programmed lower level search in almost all instances. I just can't believe that Java doesn't already have some kind of comparator that can be used for this. I mean they have comparators for sorting, but not for searching. – Lokiare Apr 23 '12 at 13:56

5 Answers5

3

If you can use Google Guava have a look at that question and its answers: Filtering on List based on one property with guava

Update:

If you don't like Google and thus don't want to use their libraries, try Apache Commons Collections' CollectionUtils:

List<Person> filteredList = new ArrayList<Person>(allPersons);
CollectionUtils.filter( filteredList, new Predicate() {
  boolean evaluate(Object object) {
    //do whatever you want
  }
});

The downside is that Commons Collections itself doesn't use Generics. There is a generic port of Commons Collections 3.1, however.

Community
  • 1
  • 1
Thomas
  • 87,414
  • 12
  • 119
  • 157
  • I prefer not to use any Google products if I have a choice due to their illegal privacy violations that all of their software uses. – Lokiare Apr 23 '12 at 13:33
  • @JamesHolloway that's true for their software products but I doubt their low level libraries do that. It's still a somewhat political choice though - as an alterative try Apache's commons collections (I'll update my answer). – Thomas Apr 23 '12 at 13:37
  • Where can I download the collectionUtils from? the link you have goes to their documentation. – Lokiare Apr 23 '12 at 13:51
  • @JamesHolloway Here's the DL-Link: http://commons.apache.org/collections/download_collections.cgi. – Thomas Apr 23 '12 at 13:53
  • Ouch. Guava is open-source for a reason. Look through its source if you like. – Louis Wasserman Apr 23 '12 at 14:28
  • @LouisWasserman I agree and personally would use Guava (although there's currently no need to - we have Apache Commons :) ). However, there are still people who don't like Google in general (for political reasons) and thus it's more of a political reason not to use Guava (that's a personal decision IMHO). – Thomas Apr 23 '12 at 14:39
  • Fair enough. It just...hurts. Contributing to Guava has been one of the most rewarding experiences of my life. – Louis Wasserman Apr 23 '12 at 14:41
  • This is the best solution. I used the code: AnimationData animation = (AnimationData) CollectionUtils.find(characterAnimations, new Predicate() { @Override public boolean evaluate(Object o) { return (((AnimationData)o).name.contentEquals(name)); } }); – Lokiare Apr 23 '12 at 14:43
  • @LouisWasserman I can imagine that it really hurts, especially since the open source libraries provided by Google normally have a really high quality. – Thomas Apr 23 '12 at 14:45
  • Yes, but no matter how high quality if enough people refuse to use their products or services they will change their data-mining policies. – Lokiare Apr 23 '12 at 16:46
1

You can always add an implementation of java.lang.Comparable for your particular case.

duffymo
  • 305,152
  • 44
  • 369
  • 561
0

I believe the Java Set (see documentation) is pretty much a direct parallel to the C++ STL versions. You can override the equals operator to set up a custom comparison.

Given a set, you could call mySet.contains(o) to see if the set contains a specified object.

In your case, you would first create a Java class representing your person, store a set of Person objects in a set, and make sure to override the equals member function to compare the first and last name, and return true if both were the same. You could then check whether or not your set contained a certain "Person" or not.

Note that it's recommended that if you override equals, you should also override hashCode.

aardvarkk
  • 14,955
  • 7
  • 67
  • 96
  • Using a set would still require you to pass the search parameter to the `contains(...)` method (there is no `get(...)` method anyways) and`equals(...)` would then have to return true for that parameter (which would most likely break the contract on `equals(...)`). – Thomas Apr 23 '12 at 13:34
  • How can I override it and also have the original comparison work? I want to search a list for an object that has a variable called "name", but I want an optimized search, instead of a for loop and contentEquals(). – Lokiare Apr 23 '12 at 13:40
  • My mistake -- my answer was based on the idea that you only had one test for equality. I suppose this doesn't really apply, because you're really trying to perform a "test" per element. Sorry! – aardvarkk Apr 23 '12 at 13:43
0

You can use

list.contains (o);

to check, whether the list contains a specific Object.

To check for a predicate, there is no easy build-in method. However, it seems more simple than your C++ setup:

for (Person p: persons) {
  if (p.firstname.equals ("John")) {  
     doSomethingWith (p);
     // if you only want to handle one case, the first John:
     break; 
  }
}

To create a "John"-Collection:

List <Person> johns = ArrayList <Person> ();
for (Person p: persons) {
  if (p.firstname.equals ("John")) {  
     johns.add (p);
  }
}
user unknown
  • 35,537
  • 11
  • 75
  • 121
  • This doesn't involve a specific comparator and doesn't retrieve the object. – Marko Topolnik Apr 23 '12 at 13:38
  • This is how I would have done it if I couldn't find an alternative, but doing this causes it to iterate through possibly the entire list, which is slow. Using the CollectionUtils from Apache is more efficient. – Lokiare Apr 23 '12 at 16:49
  • @JamesHolloway: If you want to break the loop, use `break` as shown in example 1. I'm now used to scala collections which have find, filter, takeWhile, map, foreach and more, which makes it very concise to use collections, and which can be called from Java, but it isn't idiomatic Java code. – user unknown Apr 23 '12 at 17:01
0

I believe that Java's Arrays.binarySearch(T[] a, int fromIndex, int toIndex, T key, Comparator c) will suit your needs.

Or, if you wish to use ArrayList as you stated, try Collections.binarySearch(List> list,T key, Comparator c)

Note, that you would have to sort your array before using this.

Vadym S. Khondar
  • 1,428
  • 2
  • 12
  • 19