Monday, July 2, 2007

Java Collection finder methods(spring & ruby style)

Using call-back interface(spring way), I wrote finder methods(block call method in ruby).
I hope this closure-style finding methods provide more human readable sourcecode instead of for-looping.

I've needed to search object(s) in List. In the first place, I repeated for-loops to get objects and check whether it matches criteria. Unfortunately, it was not clean-looking-sorucecode for me.

Unfortunately, only final variables are allowed in callback implementation.

Sample Usage(Junit style)

public class SearcherTest {

private List<Person> people;
private Person p1 = new Person("foo", 1);
private Person p2 = new Person("bar", 2);
private Person p3 = new Person("baz", 3);
private CollectionSearcher<Person> searcher;

@Before
public void setUp() {
people = new ArrayList<Person>();
people.add(p1);
people.add(p2);
people.add(p3);

searcher = new CollectionSearcher<Person>(people);
}


@Test
public void testFind() {
Person result = searcher.find(new SearchStrategy<Person>() {
public boolean isEqual(Person person) {
return person.getName().equals("bar");
}
});

assertSame(p2, result);
}

@Test
public void testFindAll() {
List<Person> result = searcher.findAll(new SearchStrategy<Person>() {
public boolean isEqual(Person person) {
return person.getName().startsWith("b");
}
});

assertEquals(2, result.size());
assertSame(p2, result.get(0));
assertSame(p3, result.get(1));
}

@Test
public void testCollect() {
List<Person> result = searcher.collect(new CollectStrategy<Person>() {
public Person compare(Person person) {
if (person.getId() > 2) {
return person;
}
return null;
}
});

assertEquals(1, result.size());
assertEquals(3, result.get(0).getId());
}

@Test
public void testIndexOf() {
int index = searcher.indexOf(new SearchStrategy<Person>() {
public boolean isEqual(Person person) {
return person.getName().equals("baz");
}
});

assertEquals(2, index);
}

@Test
public void testContains() {
boolean result = searcher.contains(new SearchStrategy<Person>() {
public boolean isEqual(Person person) {
return person.getName().equals("baz");
}
});
assertTrue(result);

result = searcher.contains(new SearchStrategy<Person>() {
public boolean isEqual(Person person) {
return person.getName().equals("foobar");
}
});
assertFalse(result);
}


static class Person {
private String name;
private int id;


public Person(String name, int id) {
this.name = name;
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}
}
}



Implemenatation


public class CollectionSearcher<E> {
private Collection<E> collection;

public CollectionSearcher() {
}

public CollectionSearcher(Collection<E> collection) {
this.collection = collection;
}

/**
* return the first element that given strategy returns true
*
* @param strategy
* @return
*/
public E find(SearchStrategy<E> strategy) {
for (E entry : collection) {
if (strategy.isEqual(entry)) return entry;
}
return null;
}

/**
* returns all element that given strategy returns true
*
* @param strategy
* @return
*/
public List<E> findAll(SearchStrategy<E> strategy) {
List<E> results = new ArrayList<E>();
for (E entry : collection) {
if (strategy.isEqual(entry)) results.add(entry);
}
return results;
}

/**
* return list of elements that strategy returns
* if strategy returns null, then it won't be in the list
*
* @param strategy
* @return
*/
public List<E> collect(CollectStrategy<E> strategy) {
List<E> results = new ArrayList<E>();
for (E entry : collection) {
E result = strategy.compare(entry);
if (result != null) results.add(result);
}
return results;
}

/**
* returns the first index of element that strategy returns true, or -1 if none of the element satisfies the strategy
*
* @param strategy
* @return
*/
public int indexOf(SearchStrategy<E> strategy) {
int index = 0;
for (E entry : collection) {
if (strategy.isEqual(entry)) return index;
index++;
}
return -1;
}

/**
* returns true if the collection has an element that satisfies given strategy
* @param strategy
* @return
*/
public boolean contains(SearchStrategy<E> strategy) {
for (E entry : collection) {
if (strategy.isEqual(entry)) return true;
}
return false;
}


public Collection<E> getCollection() {
return collection;
}

public void setCollection(Collection<E> collection) {
this.collection = collection;
}
}


Search Strategies

public interface CollectStrategy<T> {
T compare(T object);
}

public interface SearchStrategy<T> {
/**
* Returns true if the given object satsfies criteria
* @param object
* @return
*/
boolean isEqual(T object);
}

3 comments:

Ricky Clarkson said...

You forgot to escape your generics, the type parameters all appear undeclared.

Tadaya Tsuyu said...

Thanks Ricky,
I fixed them.

Marcos said...

Thanks, I extend your more one great entry blog to use it as array searcher using generics


import java.util.ArrayList;
import java.util.List;

/**
* Implemenatation of Search Strategies
*
* @author marcos.sousa
*/
public class ArraySearcher|E| {

private E[] collection;

public ArraySearcher() {
}

public ArraySearcher(E[] collection) {
this.collection = collection;
}

/**
* return the first element that given strategy returns true
*
* @param strategy
* @return
*/
public E find(SearchStrategy|E| strategy) {
for (E entry : collection) {
if (strategy.isEqual(entry)) return entry;
}
return null;
}

/**
* returns all element that given strategy returns true
*
* @param strategy
* @return
*/
public List|E| findAll(SearchStrategy|E| strategy) {
List|E| results = new ArrayList|E|();
for (E entry : collection) {
if (strategy.isEqual(entry)) results.add(entry);
}
return results;
}

/**
* return list of elements that strategy returns
* if strategy returns null, then it won't be in the list
*
* @param strategy
* @return
*/
public List|E| collect(CollectStrategy|E| strategy) {
List|E| results = new ArrayList|E|();
for (E entry : collection) {
E result = strategy.compare(entry);
if (result != null) results.add(result);
}
return results;
}

/**
* returns the first index of element that strategy returns true, or -1 if none of the element satisfies the strategy
*
* @param strategy
* @return
*/
public int indexOf(SearchStrategy|E| strategy) {
int index = 0;
for (E entry : collection) {
if (strategy.isEqual(entry)) return index;
index++;
}
return -1;
}

/**
* returns true if the collection has an element that satisfies given strategy
* @param strategy
* @return
*/
public boolean contains(SearchStrategy|E| strategy) {
for (E entry : collection) {
if (strategy.isEqual(entry)) return true;
}
return false;
}


public E[] getCollection() {
return collection;
}

public void setCollection(E[] collection) {
this.collection = collection;
}

}