【问题标题】:How to choose pass instance method to use to a method in Java如何选择传递实例方法以使用 Java 中的方法
【发布时间】:2022-01-18 00:18:21
【问题描述】:

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

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

但是,我还需要返回共享相同值的其他实例 我需要将 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.
}

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

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

【问题讨论】:

标签: java java-8 instance-methods


【解决方案1】:

您可以通过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(", "));
    }

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

【讨论】:

  • 感谢您的回答。这似乎正是我所需要的。我将在我的代码中实现它,并在成功运行后将其标记为答案。
  • 线性搜索不仅比排序快,而且它也不需要源列表是可变的。此外,第二个版本避免了循环内的字符串连接。
【解决方案2】:

查看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)

【讨论】:

    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

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

      创建课程对象列表

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-11-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多