Collections.frequency() 的更高级版本

More advanced version of Collections.frequency()

那么,假设我有以下列表:

List<Foo> myList = getListFromSomePlace();
int frequency = Collections.frequency(myList, someFoo);

这将计算所有 someFoo 个匹配元素。

但是,如果我有一个更"complex"的版本:

List<Foo> myList = getListFromSomePlace();
int frequency = /* get number of Elements in the List whose getInternalFoo() match a certain value */

这样做的一种方法是覆盖 Foo class 中的 equals 方法,但我真的很想避免在 Foo class 中放置自定义行为,特别是因为我可能想根据 Foo class 的不同属性获取频率,而且我只能使用一个版本的覆盖 equals 方法。

Collections.sort 这样的函数可以让我传递一个自定义比较器,它可以完全满足我的需要,但是 Collections.frequency 没有提供这个功能。

对于 Java8,我会使用流和一些 Lambda 表达式来解决这个问题,但我想看看是否有一个简单的解决方案适用于 Java 7。我我正在寻找不涉及自己编写自定义频率方法的东西,而是使用一些现有的 API。有什么事吗?

我认为标准 JDK 没有提供此功能 (Java <= 7)。如果您想要一个适用于 Java 7 且不涉及编码的解决方案,您可以使用 Guava 及其 Lists.transform 方法。

看起来像这样:

List<Foo> myList = getListFromSomePlace();
int frequency = Collections.frequency(Lists.transform(myList, new Function<Foo, MyObject>() {
    @Override
    public MyObject apply(Foo input) {
        return input.getInternalFoo();
    }
}), myCriteria); 

如果您仍然认为不值得为此添加第三方库,您仍然可以编写自己的函数接口,以及提供将 List<T> 转换为的方法的实用程序 class a List<U> 提供了要应用的映射。不是很难,也不会花那么多行代码。

编写您自己的实现将允许您一次完成此操作。

static <T, U> int frequency(Collection<T> coll, Function<? super T, ? extends U> mapper, U criteria) {
    Objects.requireNonNull(coll);
    Objects.requireNonNull(mapper);
    Objects.requireNonNull(criteria);
    int frequency = 0;
    for(T t : coll) {
        if(criteria.equals(mapper.apply(t))) {
            frequency++;
        }
    }
    return frequency;
}

我认为您无法避免编写自己的方法。如果您不想污染您的 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;
}

只需 'decorate' 您的 someFoo,根据您的要求覆盖 equals()

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;
    }
});

现在,这是有效的,因为 Collections.frequency() 实现检查对象参数是否 equals() 列表的每个元素,而不是相反。如果后者为真,则返回的频率将始终为 0.

正如您提到的 'might want to get frequency based on different properties from the Foo class',您可以将匿名内部 class 的 equals() 方法的第一部分移动到通用抽象 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);
}

然后,创建一个 ComplexFrequency 的子class 来进行比较:

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
    }
}

最后,'decorate' 您的 someFoo 使用此子 class 并将 'decorated' 实例传递给 Collections.frequency():

List<Foo> myList = getListFromSomePlace();
Foo someFoo = getSomeFooToGetItsFrequency();

int frequency = Collections.frequency(myList, new FooComparingPropertyA(someFoo));