如何选择要在 Java 中将实例方法传递给方法



>我有一个方法,它按不同的条件对列表进行排序,并返回具有最大值的名称(实例变量)。如果多个实例具有最大值,则应连接它们的所有名称。

假设我有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 函数的任意数字)。

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

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

我的任务是返回具有最大容量的课程、具有最大注册学生的课程、难度最大的课程、最大填充百分比、具有最大助教数量的课程等的字符串......

编辑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)

基于容量的排序应返回"c">

基于学生的排序应返回"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(", "));
}

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

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

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

有趣的部分是keyExtractor的类型 .粗略地说,它是一个函数,从您正在比较的对象类型到您要用于比较的字段类型。在我们的例子中,这些对应于A类和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)

时间复杂度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);
}

}

我用于测试目的的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

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

创建课程对象列表

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

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

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


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

最新更新