So, let's imagine I have the following List:
List<Foo> myList = getListFromSomePlace();
int frequency = Collections.frequency(myList, someFoo);
This will count all someFoo matching elements.
However, if I have a more "complex" version:
List<Foo> myList = getListFromSomePlace();
int frequency = /* get number of Elements in the List whose getInternalFoo() match a certain value */
One way to do so would be to override the equalsmethod in the Foo class, but I would really like to avoid putting custom behavior in the Foo class, specially because I might want to get frequency based on different properties from the Foo class, and I can only have one version of the overriden equals method.
Functions like Collections.sort would allow me to pass a custom Comparator that will do exactly what I need, but Collections.frequency doesn't provide this.
With Java8, I would use a stream and some Lambda expression to solve this issue, but I would like to see if there is a simple solution that will work with Java 7. I am looking for something that doesn't involve coding the custom frequency method myself, but using some existing API. Is there something?
I don't think you can avoid writing your own method. Make it private if you don't want to pollute your API.
public static <T> int frequency(Collection<T> c, T o, Comparator<T> comp) {
int freq = 0;
for(T e : c) {
if(o == null ? e == null : comp.compare(o, e) == 0) {
++freq;
}
}
return freq;
}
Just 'decorate' your someFoo by overriding equals() as per your requirements:
List<Foo> myList = getListFromSomePlace();
final Foo someFoo = getSomeFooToGetItsFrequency();
int frequency = Collections.frequency(myList, new Foo() {
@Override
public boolean equals(Object another) {
if (another == someFoo) {
return true;
}
if ((another == null) || (someFoo == null)) {
return false;
}
if (another.getClass() != someFoo.getClass()) {
return false;
}
Foo anotherFoo = (Foo) another;
// Compare someFoo to anotherFoo as you wish here
return comparisonResult;
}
});
Now, this works because Collections.frequency() implementation checks if the object argument equals() every element of the list and not the other way round. If the latter were true, the returned frequency would be always 0.
As you mentioned you 'might want to get frequency based on different properties from the Foo class', you could move the first part of the equals() method of the anonymous inner class to a generic abstract class:
public abstract class ComplexFrequency<T> {
private final T self;
public ComplexFrequency(T self) {
this.self = self;
}
@Override
public boolean equals(Object another) {
if (another == this.self) {
return true;
}
if ((another == null) || (this.self == null)) {
return false;
}
if (another.getClass() != this.self.getClass()) {
return false;
}
// Let subclasses compare both objects
return this.equals(this.self, (T) another);
}
protected abstract boolean equals(T self, T another);
}
Then, create a subclass of ComplexFrequency that compares as you wish:
public class FooComparingPropertyA extends ComplexFrequency<Foo> {
public FooComparingPropertyA(Foo someFoo) {
super(someFoo);
}
@Override
protected boolean equals(Foo self, Foo another) {
// check equality based on propertyA
}
}
And finally, 'decorate' your someFoo using this subclass and pass the 'decorated' instance to Collections.frequency():
List<Foo> myList = getListFromSomePlace();
Foo someFoo = getSomeFooToGetItsFrequency();
int frequency = Collections.frequency(myList, new FooComparingPropertyA(someFoo));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With