有没有办法减少这个 class 中的循环数量?

Is there a way to reduce the amount of loops in this class?

我有一个 class,其中有几种方法循环遍历 产品列表

我想知道是否有办法减少循环次数,因为它们真的只做一次检查?

某种实现函数并传递谓词的方法?我昨天发现了 Lambdas,但不确定它是否适用于此。

public class Stock {
    private ArrayList<Products> _productList;
    public Stock(){}

    public Boolean addProduct(Products product){
        return _productList.contains(product) && _productList.add(product);
    }

    public int obtainPos(int code){
        for(int i=0; i < _productList.size(); i++)
            if(_productList.get(i).getCode() == code)
                return i;
        return -1;
    }

    public void removeProduct(int code){
        int pos = obtainPos(code);
        if(pos >=0)
            _productList.remove(pos);
        else
            System.out.println("Error - Product not found.");
    }

    public int productAmount(){ return _productList.size(); }

    public int amountType(String type){
        int i = 0;
        for(Products pr : _productList)
            if(pr.getClass().getSimpleName().equals(type))
                i++;
        return i;
    }

    @Override
    public String toString(){
        StringBuilder sb = new StringBuilder();
        for(Products pr : _productList)
            sb.append(pr.getName()).append(" \n");
        return sb.toString();
    }

    public void itemsToRemove(String reason){
        StringBuilder st = new StringBuilder();
        for(Products pr : _productList)
            st.append(pr.getName()).append(" - ").append(pr.withdraw(reason)).append("\n");

        System.out.println(st.toString());
    }
}

您可以在所需的单循环中使用类似 if 语句的语句来决定您要执行的操作,例如:

public void loopClass(int decision) {
for(Products pr : _productList) {
if (decision == choice1) {
//do something
}

if (decision == choice2) {
//do something else
}

//etc etc
}

}

查看您的代码,我没有发现您设置循环的方式有任何问题。您现在的设置方式看起来非常简洁明了。 我建议保持原样。

您可以使用 HashMap<Integer,Products> 而不是 ArrayList<Products>。这将允许您消除 obtainPos 并将 addProductremoveProduct 减少到 O(1) 操作。

a way to reduce the amount of loops

Some sort of way to implement a function and pass predicate? I discovered Lambdas yesterday, but not sure it would be applicable here.

如果您正在考虑使用 lambdas 作为可能的选项,您可以使此代码更加简洁。

只需要非常基础的知识关于Java8lambda表达式。作为第一步,我建议您熟悉这个 tutorial.

让我们一一重构这些方法。

  • removeProduct()

您可以将方法 removeProduct()obtainPos() 中的所有代码替换为 单行 通过使用 Collection 接口的方法 removeIf(),像这样:

products.removeIf(product -> product.getCode() == code);

方法removeIf()需要一个Predicate一个产生布尔值的函数),它会删除与给定谓词匹配的每个元素.

此方法 returns true 如果集合被修改并且 false 否则。


  • getAmountByType()

可以使用仅包含两个操作的微小流来实现此方法:filter()count()

为了创建以产品列表作为源的流,您必须调用列表中的方法stream()

方法filter()是一个中间操作(产生新流的操作)。与 removeIf() 一样,它需要一个 谓词 ,与 removeIf() 相反,它会在结果流中只保留与给定谓词匹配的元素。

而方法count()是终端操作(关闭流的操作,return是一个值或执行最终操作,如 forEach) 即 return 流中元素的数量为 long.


  • toString()

这个方法也归结为一个 stream,它包含两个操作:map()collect()

方法map()是一个中级操作。它需要一个 Function 一个接受一个对象并生成另一个对象的函数,通常是不同类型的 )。我们可以通过使用 lambda 表达式( 如下代码所示)或使用 method reference Product::getName 来实现从产品中提取名称的函数.

所以在这种情况下,map() 会将产品流 Stream<Product> 转换为产品名称流 Stream<String>

方法count()终端操作,它需要一个Collector一个特殊的对象,它用元素填充可变容器流并产生流管道执行的结果).

这里我们可以使用 built-in 收集器 Collectors.joining(),它旨在将 String 个元素连接成一个字符串。


  • addProduct()

虽然此方法可以添加新产品,但其中有一个逻辑流程:

  1. 如果产品列表已经包含给定产品,它将 return truefalse 产品(这有点违反直觉).
  2. 它将始终将给定产品添加到列表中,无论它是否重复。因此,检查给定产品是否已存在于列表中的过程变得毫无意义。

要修复它,可以用两种方式重新实现该方法:

  • 允许重复并添加每个给定的产品;
  • 丢弃重复的产品。

在这两种情况下,实现都是一条语句:

return products.add(product);

但是为了拒绝重复,您需要将基础集合设为 HashSet请注意,此更改 不会影响 无论如何,此处列出的其余代码 )。假设 hashCode/equals 合同在 Product class 中正确实施,方法 add 将 return false 如果重复,并且 true 如果修改了一组(即成功添加了产品)。

public class ProductInStock {
    private Set<Product> products = new HashSet<>(); // if you change it to List<Products> products = new ArrayList<>(); nothing will break

    public boolean addProduct(Product product) {
        return products.add(product);
    }

    public boolean removeProduct(int code){
        return products.removeIf(product -> product.getCode() == code);
    }

    public int productAmount(){
        return products.size();
    }

    public int getAmountByType(String type) {
        return (int) products.stream()
                .filter(product -> product.getType().equals(type))
                .count();
    }

    @Override
    public String toString() {
        return products.stream()
                .map(product -> product.getName())
                .collect(Collectors.joining("\n"));
    }
}

main() - 一个小演示。

public static void main(String[] args) {
    ProductInStock productInStock = new ProductInStock();

    productInStock.addProduct(new Product("Milk", 11, "dairyProducts"));
    productInStock.addProduct(new Product("Milk", 11, "dairyProducts")); // duplicate
    productInStock.addProduct(new Product("Milk", 11, "dairyProducts")); // duplicate
    productInStock.addProduct(new Product("Cottage cheese", 12, "dairyProducts"));
    productInStock.addProduct(new Product("Cream cheese", 13, "dairyProducts"));
    productInStock.addProduct(new Product("Bread", 21, "bakedProducts"));
    productInStock.addProduct(new Product("Muffin", 22, "bakedProducts"));
    productInStock.addProduct(new Product("Cookies", 23, "bakedProducts"));

    System.out.println("Amount by type");
    System.out.println("Baked Products:\t" + productInStock.getAmountByType("bakedProducts"));
    System.out.println("Dairy Products:\t" + productInStock.getAmountByType("dairyProducts"));

    productInStock.removeProduct(12);
    productInStock.removeProduct(21);

    System.out.println("\nProduct Stock after removal:\n" + productInStock);
}

输出

Amount by type
Baked Products: 3
Dairy Products: 3

Product Stock after removal:
Muffin
Cookies
Milk
Cream cheese

旁注:

  • 下划线的使用 _ 如果 Java 中变量名的前面不符合 命名约定 。在需要区分共享相同名称的参数和字段的情况下,我们使用关键字 this.
  • 产品类型 enum 表示而不是依赖字符串值会更方便。

正如您提到的 lambda,是的,结合 StreamList.removeIf 它们会有所帮助。不幸的是,它们是一项高级功能,可能会稍后处理。 展示它会对 i.a 做什么。你的 for-loops:

public class Stock {
    private final List<Product> productList = new ArrayList<>();

    public Stock() {
    }

    public Stock(List<Product> productList) {
        this.productList.addAll(productList);
    }

    public boolean addProduct(Product product) {
        return productList.contains(product) || productList.add(product);
    }

    //public int obtainPos(int code){
    public Optional<Product> obtainByCode(int code) {
        productList.stream()
                .filter(pr -> pr.getCode() == code)
                .findAny();
    }

    public void removeProduct(int code) {
        obtainByCode(code).ifPresentOrElse(pr -> productList.remove(pr),
                () -> System.out.println("Error - Product not found."));
    }

    public int productAmount() {
        return productList.size();
    }

    public int amountType(String type) {
        return (int) productList.stream()
                .filter(pr -> pr.getClass().getSimpleName().equals(type))
                .count();
    }

    @Override
    public String toString() {
        return productList.stream()
                .map(Product::getName)
                .map(nm -> nm + " \n")
                .collect(Collectors.joining());
    //    return productList.stream()
    //            .map(Product::getName)
    //            .collect(Collectors.joining(" \n"); // At the end without \n.
    }

    public void itemsToRemove(String reason) {
        String items = productList.stream()
                .map(Product::getName)
                .map(nm -> String.format("%s - %s\n", nm, reason))
                .collect(Collectors.joining(" \n");
        System.out.println(items);
    }
}

备注:

  • 我假设名字 Products 应该是 Product
  • addProduct 使用了 AND (THEN) &&,我认为你的意思是 OR (ELSE) ||。当产品已经存在时,添加的产品将被丢弃。不确定是否需要这样做。 Product.equals(在代码上?)必须存在。
  • obtainPos 引入了一个内部动态特性(当调用 removeProduct 时)。更好的 return Optional<Product> 这是一个 type-safe 包装器;见 ifPresent.
  • 像下划线这样的前缀在 java 中没有使用。您可以使用 this.field.
  • 消除相同命名参数的歧义
  • 未更改(=未替换)的字段可以是 final
  • 针对接口 (List) 而不是实现 class (ArrayList) 进行编程更具表现力。
  • 有像int, boolean, char, long这样的原始类型。还有 wrapper classes Integer, Boolean, Character, Long。使用原始类型更符合逻辑。只有通用参数类型不能这样做:List<Integer>.
  • lambda 是 pr -> pr.getName() 或方法引用 Product::getName

已经提到 Map<Integer, Product> productsByCode = new HashMap<>(); 证明可以通过代码快速访问产品。

你们的for循环都是完全不同的,如此多的收获是难以置信的。但是 Stream 隔离条件等,并且可能更灵活。