有没有办法减少此类中的循环量?
我有一个类,其中有几个方法在产品列表上循环。
我想知道是否有一种方法可以减少循环量,因为它们实际上只进行一次检查?
某种方式来实现函数并传递谓词?我昨天发现了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());
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
如果您正在考虑使用lambda作为一种可能的选择,则可以使此代码更加简洁。
它仅需要有关 Java 8 lambda 表达式 和流主题的非常基本的知识。作为第一步,我建议您熟悉这个教程。
我们来一一重构这些方法。
removeProduct()
您可以用一行代码替换
removeProduct()
和obtainPos()
方法中的所有代码 通过使用方法Collection 接口的“nofollow noreferrer">removeIf()
,如下所示:方法
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()
虽然此方法能够添加新产品,但其中有一个逻辑流程:
true
,并且false
对于新产品(这有点违反直觉)。为了解决这个问题,可以通过两种方式重新实现该方法:
在这两种情况下,实现都是一条语句:
但是为了拒绝重复,您需要将底层集合设为
HashSet
(请注意,此更改将不会影响)。假设在Product
类中正确实现了hashCode/equals
合约,则方法add
将在以下情况下返回false
重复,如果修改了集合(即产品已成功添加),则为true
。main()
- 一个小演示。输出
旁注:
_
>。如果需要区分具有相同名称的参数和字段,我们可以使用关键字this
。枚举
表示而不是依赖于字符串值会更方便。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()
andobtainPos()
with a single line by using methodremoveIf()
of theCollection
interface, like that:Method
removeIf()
expects aPredicate
(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 andfalse
othewise.getAmountByType()
This method could be implemented by using a tiny stream containing only two operations:
filter()
andcount()
.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 asremoveIf()
it expects a predicate, by conversely toremoveIf()
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, likeforEach
) that return the number of element in the stream aslong
.toString()
This method also boils down to a stream that consists of two operations:
map()
andcollect()
.Method
map()
is an intermediate operation. It expects aFunction
(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 referenceProduct::getName
.So in this case,
map()
will transform a stream of productsStream<Product>
into a stream of product namesStream<String>
.Method
count()
is terminal operation, that expects aCollector
(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 joinString
elements into a single string.addProduct()
Although this method is capable of adding new products, there's a logical flow in it:
true
if the list of products already contains the given product, andfalse
for a new product (that's a bit counterintuitive).To fix it, the method could be reimplemented in two ways:
In both cases implementation is a single statement:
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 thathashCode/equals
contract was correctly implemented inProduct
class, methodadd
will returnfalse
in case of a duplicate, andtrue
if a set was modified (i.e. the product was successfully added).main()
- a small demo.Output
Sidenotes:
_
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 wordthis
.enum
instead of relying on string values.您可以在所需的单个循环内添加类似 if 语句的内容,以决定您想要执行的操作,例如:
查看您的代码,我认为您设置循环的方式没有任何问题。您现在的设置方式看起来非常简洁明了。 我建议保持原样。
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:
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.
您可以使用
HashMap
而不是ArrayList
。这将允许您消除obtainPos
并将addProduct
和removeProduct
操作减少到 O(1) 操作。You could use a
HashMap<Integer,Products>
instead of anArrayList<Products>
. That would allow you to eliminateobtainPos
and reduceaddProduct
andremoveProduct
to O(1) operations.正如您提到的 lambda,是的,与 Stream 或 List.removeIf 结合使用它们会有所帮助。不幸的是,它们是一项高级功能,可能会稍后处理。
为了展示它会对 for 循环执行什么操作:
备注:
Products
应该是Product
。addProduct
使用了 AND (THEN)&&
,我认为您的意思是 OR (ELSE)||
。当产品已经存在时,添加的产品将被丢弃。不确定这是否是所期望的。Product.equals
(在代码上?)必须存在。obtainPos
引入了一个内部动态功能(当调用removeProduct
时)。更好地返回Optional
这是一个类型安全的包装器;请参阅ifPresent
。this.field
消除相同命名参数的歧义。最终
。List
)而不是实现类(ArrayList
)进行编程更具表现力。Integer、Boolean、Character、Long
。使用原始类型更符合逻辑。仅对于泛型参数类型不能这样做:List
。Product::getName
。已经提到的是
Map; productsByCode = new HashMap<>();
将证明通过代码可以快速访问 Product。您拥有的
for
循环完全不同,因此获得如此多的收益是令人难以置信的。然而,流隔离了条件等,并且可能更灵活。As you mention lambdas, yes in combination with
Stream
orList.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:
Remarks:
Products
should have beenProduct
.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. AProduct.equals
(on code?) must exist.obtainPos
introduces an internal dynamic feature (whenremoveProduct
is called). Better returnOptional<Product>
which is a type-safe wrapper; seeifPresent
.this.field
.final
.List
) instead of the implementing class (ArrayList
).int, boolean, char, long
. And there are wrapper classesInteger, Boolean, Character, Long
. Use the primitive types as more logical. Only for generic parameter types this cannot be done:List<Integer>
.pr -> pr.getName()
or a method referenceProduct::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.