有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java更高级版本的集合。频率()

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

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

这将统计所有someFoo匹配元素

但是,如果我有一个更“复杂”的版本:

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

一种方法是重写Foo类中的equals方法,但我确实希望避免在Foo类中放置自定义行为,特别是因为我可能希望根据Foo类的不同属性获取频率,并且我只能有一个版本的被重写的equals方法

Collections.sort这样的函数将允许我传递一个定制的比较器,该比较器将完全满足我的需要,但不支持集合。频率不能提供这一点

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


共 (3) 个答案

  1. # 1 楼答案

    我认为你不能避免写你自己的方法。如果你不想污染你的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;
    }
    
  2. # 2 楼答案

    根据您的要求,只需通过覆盖equals()来“装饰”您的someFoo

    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

    正如您提到的,您“可能希望根据Foo类的不同属性获取频率”,您可以将匿名内部类的equals()方法的第一部分移动到通用抽象类:

    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的子类,根据需要进行比较:

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

    最后,使用此子类“装饰”您的someFoo,并将“装饰”实例传递给Collections.frequency()

    List<Foo> myList = getListFromSomePlace();
    Foo someFoo = getSomeFooToGetItsFrequency();
    
    int frequency = Collections.frequency(myList, new FooComparingPropertyA(someFoo));
    
  3. # 3 楼答案

    我不认为标准JDK提供了这一点(Java<;=7)。如果您想要一个与Java7一起工作且不涉及编码的解决方案,那么可以使用Guava及其^{}方法

    看起来是这样的:

    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>转换为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;
    }