有没有办法减少此类中的循环量?

发布于 2025-01-17 04:13:54 字数 1644 浏览 4 评论 0 原文

我有一个类,其中有几个方法在产品列表上循环。

我想知道是否有一种方法可以减少循环量,因为它们实际上只进行一次检查?

某种方式来实现函数并传递谓词?我昨天发现了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());
    }
}

I have a class where several methods loop over the list of products.

I am wondering if there would be a way to reduce the amount of loops as they only do one check really?

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

和我恋爱吧 2025-01-24 04:13:54

减少循环量的方法

某种方式来实现函数并传递谓词?我
昨天发现了 Lambda,但不确定它是否适用
在这里。

如果您正在考虑使用lambda作为一种可能的选择,则可以使此代码更加简洁。

它仅需要有关 Java 8 lambda 表达式主题的非常基本的知识。作为第一步,我建议您熟悉这个教程

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

  • removeProduct()

您可以用一行代码替换 removeProduct()obtainPos() 方法中的所有代码 通过使用方法Collection 接口的“nofollow noreferrer">removeIf(),如下所示:

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

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

如果集合被修改,此方法返回 true,否则返回 false


  • getAmountByType()

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

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

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

方法 count() 是终端操作(关闭流并返回值或执行最终操作的操作,例如 forEach),返回流中元素的数量为long


  • toString()

此方法还可以归结为一个,它由两个操作组成:map()collect().

方法map()是一个中间操作。它需要一个函数一个函数,它接受一个对象并生成另一个对象,通常是不同类型的)。我们可以通过使用 lambda 表达式(如下面的代码所示)或使用 方法参考 Product::getName

因此,在本例中,map() 会将产品流 Stream 转换为产品名称流 Stream

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

在这里,我们可以使用内置收集器 Collectors.joining() ,它旨在将 String 元素连接到单个字符串中。


  • addProduct()

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

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

为了解决这个问题,可以通过两种方式重新实现该方法:

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

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

return products.add(product);

但是为了拒绝重复,您需要将底层集合设为HashSet请注意,此更改将不会影响)。假设在 Product 类中正确实现了 hashCode/equals 合约,则方法 add 将在以下情况下返回 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
  • 产品类型枚举表示而不是依赖于字符串值会更方便。

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.

If you're considering usage of lambdas as a possible option, you can make this code much more concise.

It requires only very basic knowledge on the topic of Java 8 lambda expressions and streams. As a first step, I suggest you to get familiar with this tutorial.

Let's refactor these methods one by one.

  • removeProduct()

You can substitute all the code inside the methods removeProduct() and obtainPos() with a single line by using method removeIf() of the Collection interface, like that:

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

Method removeIf() expects a Predicate (a function that produces a boolean value), and it'll remove every element that matches the given predicate.

This method returns true if collection was modified and false othewise.


  • getAmountByType()

This method could be implemented by using a tiny stream containing only two operations: filter() and count().

In order to create a stream with a list of products as a source, you have to invoke method stream() on the list.

Method filter() is an intermediate operation (operation that produces a new stream). As well as removeIf() it expects a predicate, by conversely to removeIf() it will preserve in the resulting stream only elements that match the given predicate.

And method count() is terminal operation (operation that closes the stream and returns a value or does a final action, like forEach) that return the number of element in the stream as long.


  • toString()

This method also boils down to a stream that consists of two operations: map() and collect().

Method map() is an intermediate operation. It expects a Function (a function that takes an object and produces another object, usually of a different type). We can implement the function that extracts the name from a product either by using a lambda expression (as shown in the code below), or with a method reference Product::getName.

So in this case, map() will transform a stream of products Stream<Product> into a stream of product names Stream<String>.

Method count() is terminal operation, that expects a Collector (a special object that populates a mutable container with elements of the stream and produces a result of the execution of the stream pipeline).

Here we can use a built-in collector Collectors.joining() which is designed to join String elements into a single string.


  • addProduct()

Although this method is capable of adding new products, there's a logical flow in it:

  1. It will return true if the list of products already contains the given product, and false for a new product (that's a bit counterintuitive).
  2. It will always add the given product to the list, regardless whether it's a duplicate or not. Because of that, the process of checking if the given product already exists in the list becomes pointless.

To fix it, the method could be reimplemented in two ways:

  • allow duplicates and add every given product;
  • discard duplicated products.

In both cases implementation is a single statement:

return products.add(product);

But in order to reject duplicates, you need to make the underlying collection to be a HashSet (note that this change will not affect anyhow the rest code listed here). Assuming that hashCode/equals contract was correctly implemented in Product class, method add will return false in case of a duplicate, and true if a set was modified (i.e. the product was successfully added).

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() - a small demo.

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

Output

Amount by type
Baked Products: 3
Dairy Products: 3

Product Stock after removal:
Muffin
Cookies
Milk
Cream cheese

Sidenotes:

  • Usage of underscore _ if front of the variable names in Java isn't aligned with the naming conventions. In cases when it's necessary to distinguish between a parameter and a field that share the same name, we have the key word this.
  • It'll be more convenient to make the product type to be represented by an enum instead of relying on string values.
近箐 2025-01-24 04:13:54

您可以在所需的单个循环内添加类似 if 语句的内容,以决定您想要执行的操作,例如:

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

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

//etc etc
}

}

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

You could have something like an if statement inside your desired single loop that decides between the actions you'd like to take, for example:

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

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

//etc etc
}

}

Looking at your code I don't see anything wrong with the way you have loops set up though. It seems pretty concise and clear the way you have it set up now. I would recommend leaving it as is.

一念一轮回 2025-01-24 04:13:54

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

You could use a HashMap<Integer,Products> instead of an ArrayList<Products>. That would allow you to eliminate obtainPos and reduce addProduct and removeProduct to O(1) operations.

山人契 2025-01-24 04:13:54

正如您提到的 lambda,是的,与 Stream 或 List.removeIf 结合使用它们会有所帮助。不幸的是,它们是一项高级功能,可能会稍后处理。
为了展示它会对 for 循环执行什么操作:

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 时)。更好地返回 Optional 这是一个类型安全的包装器;请参阅ifPresent
  • 像下划线这样的前缀在java中不使用。您可以使用 this.field 消除相同命名参数的歧义。
  • 未更改(=未替换)的字段可以是最终
  • 针对接口(List)而不是实现类(ArrayList)进行编程更具表现力。
  • 有一些基本类型,如 int、boolean、char、long。还有包装类Integer、Boolean、Character、Long。使用原始类型更符合逻辑。仅对于泛型参数类型不能这样做:List
  • lambda 可以是 pr -> 或 pr -> 。 pr.getName() 或方法引用 Product::getName

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

您拥有的 for 循环完全不同,因此获得如此多的收益是令人难以置信的。然而,流隔离了条件等,并且可能更灵活。

As you mention lambdas, yes in combination with Stream or List.removeIf they help. Unfortunately they are an advanced feature, probably treated a bit later.
To show what it would do to i.a. your 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);
    }
}

Remarks:

  • I assumed that the name Products should have been Product.
  • addProduct used an AND (THEN) && where I think you meant OR (ELSE) ||. When the product already exists the added product is discarded. Not sure whether that is desired. A Product.equals (on code?) must exist.
  • obtainPos introduces an internal dynamic feature (when removeProduct is called). Better return Optional<Product> which is a type-safe wrapper; see ifPresent.
  • Prefixes like that underscore are not usance in java. You can disambiguate with same named parameters with this.field.
  • Unchanged (=unreplaced) fields can be final.
  • It is more expressive to program against interfaces (List) instead of the implementing class (ArrayList).
  • There are primitive types like int, boolean, char, long. And there are wrapper classes Integer, Boolean, Character, Long. Use the primitive types as more logical. Only for generic parameter types this cannot be done: List<Integer>.
  • A lambda is either pr -> pr.getName() or a method reference Product::getName.

Already mentioned is that Map<Integer, Product> productsByCode = new HashMap<>(); would prove a fast access of a Product by code.

The for loops you have are all quite different, so much gain is implausible. However a Stream isolates conditions and such, and might be more flexible.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文