如何选择传递实例方法以用于 Java 中的方法

How to choose pass instance method to use to a method in Java

我有一种方法可以根据不同的标准对列表进行排序,returns 是具有最大值的列表的名称(实例变量)。如果超过一个实例具有最大值,则应将它们的所有名称连接起来。

假设我有 Class A 如下。

Class A {
    ...
    String getName(){...}
    int getValue1() {...}
    int getValue2() {...}
    ...
    int getValueN() {...}
    ...
}

我有a List<A> listToSort。我通常会将此列表排序为 listToSort.sort(Comparator.comparing(A::getValue1))listToSort.sort(Comparator.comparing(A::getValue2)),依此类推。然后取共享最大值的。

在一种方法中,我认为应该这样做:

String getMaxString (Comparator c) {
    listToSort.sort(c);
    ...
}

并发送 Comparator.comparing(A.getValueX) 作为参数以使用不同的方法调用它。 (此处的 X 表示 getValue 函数的任意数字)。

但是,我还需要 return 其他实例共享相同的值 我需要将 Class 方法传递给我的方法并调用实例:

String getMaxString (Comparator c) {
    listToSort.sort(c);

    int maxValue = listToSort.get(listToSort.size() - 1).getValueX();
    String maxString = listToSort.get(listToSort.size() - 1).getName();

    for (int i = listToSort.size() - 2; i >= 0; i--) {
        if (listToSort.get(i).getValueX()() == maxValue) {
            maxString += ", " + listToSort.get(i).getName();
        }
    }
    return maxString;
}

我如何传递这个方法来调用这里的实例?还是我需要考虑其他方式?

编辑:

我有一个课程列表 List<Course> mylist 其中一门课程可以简化为:

Class Course {
    private String name;
    private int capacity;
    private int students;
    
    ...
    //bunch of getters.
}

我的任务是 return 具有最大容量的课程的字符串,具有最大注册学生的课程,最困难的课程,最大填充百分比,助教数量最多的课程等...

编辑 2: 如评论区所要求。

名单

Course a (name "a", capacity 10, students 5)
Course b (name "b", capacity 20, students 5)
Course c (name "c", capacity 30, students 0)

根据容量排序应该return "c"

根据学生排序应该return "a b"

您可以通过 getter 方法并在 getMaxString 中创建比较器:

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.function.Function;

public class Foo {
    static class AClass {
        private final String name;
        private final int value1;
        private final int value2;

        String getName() { return name; }
        int getValue1() { return value1; }
        int getValue2() { return value2; }

        public AClass(String name, int value1, int value2) {
            this.name = name;
            this.value1 = value1;
            this.value2 = value2;
        }
    }

    static String getMaxString(Function<AClass,Integer> f, List<AClass> listToSort) {
        listToSort.sort(Comparator.comparing(f));
        int maxValue = f.apply(listToSort.get(listToSort.size() - 1));
        String maxString = listToSort.get(listToSort.size() - 1).getName();

        for (int i = listToSort.size() - 2; i >= 0; i--) {
            if (f.apply(listToSort.get(i)) == maxValue) {
                maxString += ", " + listToSort.get(i).getName();
            }
        }
        return maxString;
    }

    public static void main(String[] args) {
        List<AClass> list = new ArrayList<>();
        list.add(new AClass("a", 1,2));
        list.add(new AClass("b", 1,2));
        list.add(new AClass("c", 2,1));
        list.add(new AClass("d", 2,1));
        System.out.println(getMaxString(AClass::getValue1, list));
        System.out.println(getMaxString(AClass::getValue2, list));
    }
}

正如 Tim Moore 上面建议的那样,没有必要对列表进行排序(成本为 O(n*log n)),我们可以遍历它两次:

    static String getMaxString2(ToIntFunction<AClass> f, List<AClass> listToSort) {
        int maxValue = listToSort.stream().mapToInt(f).max().orElseThrow();
        return listToSort.stream()
          .filter(a -> maxValue == f.applyAsInt(a))
          .map(AClass::getName)
          .collect(Collectors.joining(", "));
    }

请注意,您应该使用空列表测试您的代码。

时间复杂度 O(n) - 数据集只有一次迭代。

希望对您有所帮助。 有什么不明白的可以留言提问。

主要

public class MaxClient {
public static void main(String[] args) {
    Comparator<A> comp = Comparator.comparingInt(A::getVal1);

    List<A> items = List.of(new A(1, 8), new A(2, 8), new A(5, 8), new A(5, 27), new A(3, 8));
    items.stream()
            .collect(new GetMax(comp))
            .forEach(System.out::println);
}

}

自定义收集器GetMax

public class GetMax implements Collector <A, Deque<A>, Deque<A>> {
private final Comparator<A> comp;

public GetMax(Comparator<A> comp) {
    this.comp = comp;
}

@Override
public Supplier<Deque<A>> supplier() {
    return ArrayDeque::new;
}

@Override
public BiConsumer<Deque<A>, A> accumulator() {
    return (stack, next) -> {
        if (!stack.isEmpty() && comp.compare(next, stack.peekFirst()) > 0) stack.clear();
        if (stack.isEmpty() || comp.compare(next, stack.peekFirst()) == 0) stack.offerLast(next);
    };
}

@Override
public BinaryOperator<Deque<A>> combiner() {
    return (stack1, stack2) -> {
        if (stack1.isEmpty()) return stack2;
        if (stack2.isEmpty()) return stack1;
        if (comp.compare(stack1.peekFirst(), stack2.peekFirst()) == 0) {
            stack1.addAll(stack2);
        }
        return stack1;
    };
}

@Override
public Function<Deque<A>, Deque<A>> finisher() {
    return stack -> stack;
}

@Override
public Set<Characteristics> characteristics() {
    return Set.of(Characteristics.UNORDERED);
}

}

Class A 我用于测试目的

public class A {
private int val1;
private int val2;

public A(int val1, int val2) {
    this.val1 = val1;
    this.val2 = val2;
}

public int getVal1() {
    return val1;
}

public int getVal2() {
    return val2;
}

@Override
public String toString() {
    return "A val1: " + val1 + " val2: " + val2;
}

}

输出

A val1: 5 val2: 8
A val1: 5 val2: 27

查看 Comparator.comparing 的类型签名很有用,因为听起来您想做类似的事情:

static <T,U extends Comparable<? super U>> Comparator<T> comparing(Function<? super T,? extends U> keyExtractor)

有趣的部分是 keyExtractor 的类型。粗略地说,它是从您正在比较的对象的类型到您要用于比较的字段的类型的函数。在我们的例子中,这些对应于 A class 和 Integer。因为这些类型在这个例子中是固定的,所以你可以用这样的签名声明一个方法:

String getMaxString(Function<A, Integer> property)

现有的算法可以这样使用:

String getMaxString(Function<A, Integer> property) {
    listToSort.sort(Comparator.comparing(property));

    int maxValue = property.apply(listToSort.get(listToSort.size() - 1));
    String maxString = listToSort.get(listToSort.size() - 1).getName();

    for (int i = listToSort.size() - 2; i >= 0; i--) {
        if (listToSort.get(i).getValueN() == maxValue) {
            maxString += ", " + listToSort.get(i).getName();
        }
    }
    return maxString;
}

但是,不必对整个列表进行排序以确定最大元素,因为这可以通过遍历列表一次来确定:

String getMaxString(Function<A, Integer> property) {
    int maxValue = Integer.MIN_VALUE;
    StringBuilder maxString = new StringBuilder();

    for (A element : listToSort) {
        int currentValue = property.apply(element);
        if (currentValue > maxValue) {
            // there is a new maximum, so start the string again
            maxString = new StringBuilder(element.getName());
            maxValue = currentValue;
        } else if (currentValue == maxValue) {
            // equal to the existing maximum, append it to the string
            if (maxString.length() > 0) {
                maxString.append(", ");
            }
            maxString.append(element.getName());
        }
        // otherwise, it's less than the existing maximum and can be  ignored 
    }

    return maxString.toString();
}

无论哪种方式,您都可以使用相同的方法引用语法调用它:

getMaxString(A::getValueN)

感谢您发布我要求的信息。这是我想出的。

创建课程对象列表

List<Course> list = List.of(
      new Course("a", 10, 5),
      new Course("b", 20, 5),
      new Course("c", 30, 0));

流式传输方法并将它们应用于列表

List<String> results = Stream.<Function<Course, Integer>>of(
    Course::getCapacity, 
    Course::getStudents)
      .map(fnc-> getMaxString(fnc, list))
      .toList();
    
results.forEach(System.out::println);

打印结果

c
a b

我写了一个简单的方法,它采用方法引用和列表并找到最大值。它不做任何排序。

  • 分配一个列表来保存名字
  • 将最大值设置为尽可能低的值
  • 遍历应用该方法的列表。
  • 如果该值大于当前最大值,则替换它并清除当前名称列表。
  • 否则,如果相等,则添加一个新名称。
  • 完成后,return 格式化字符串。

    
static String getMaxString(Function<Course, Integer> fnc,
        List<Course> list) {
    List<String> result = new ArrayList<>();
    int max = Integer.MIN_VALUE;
    for (Course obj : list) {
        int val = fnc.apply(obj);
        if (val >= max) {
            if (val > max) {
                result.clear();
            }
            max = val;
            result.add(obj.getName());
        }
    }
    return String.join(" ", result);
}