
本文旨在指导开发者如何在Java中使用`ArrayList
在许多业务场景中,我们需要收集和管理一系列订单或产品项。一个常见的实现方式是使用Java的ArrayList来存储这些项。当每个项可以简单地由几个基本数据表示时,例如产品ID和其对应的数量,我们可能会选择使用一维数组int[]来封装这些信息,并将其存储在ArrayList<int[]>中。
例如,一个订单项可以表示为int[] order = {idConso, nbrConso},其中idConso是产品ID,nbrConso是该产品的数量。我们的目标是构建一个系统,当用户输入一个产品ID时:
在尝试解决上述问题时,开发者可能会自然地想到使用ArrayList.contains()方法来检查产品ID是否存在。然而,直接使用ord.contains(Order[0] == idConso)或类似表达式是无效的,原因如下:
立即学习“Java免费学习笔记(深入)”;
因此,依赖ArrayList.contains()来检查int[]数组中特定索引的值是不可行的。
要正确实现产品ID的查找和数量更新,我们需要手动遍历ArrayList中的每个int[]元素,并检查其产品ID。
实现步骤:
示例代码片段:
// flag if idConso was found in ord
boolean found = false;
for (int[] addedOrder : ord) { // 遍历ArrayList中的每一个int[]
if (addedOrder[0] == idConso) { // 检查当前int[]的第一个元素(产品ID)
// 找到了匹配的产品ID
System.out.println("包含该产品,更新数量。");
addedOrder[1] += nbrConso; // 更新该订单项的数量
found = true; // 设置标志为true
break; // 找到后即可跳出循环
}
}
// 如果循环结束后仍未找到匹配项,则添加新的订单
if (!found) {
ord.add(Order); // 添加新的订单数组
System.out.println("新产品,已添加。");
}在处理集合中的数组或对象时,一个非常常见的错误是关于对象引用的理解。在原始代码中,int[] Order = new int[2];被声明在do-while循环的外部。这意味着在整个循环过程中,Order变量始终引用的是同一个int[2]数组对象。
后果:
当你多次执行ord.add(Order);时,实际上是将对同一个Order数组对象的引用多次添加到了ArrayList中。这意味着ArrayList中的所有条目都指向内存中的同一个int[]数组。因此,当你修改Order数组的任何元素(例如Order[0] = idConso;或Order[1] = nbrConso;)时,所有ArrayList中指向该数组的引用都会反映这些修改。这显然不是我们期望的行为,因为我们希望每个订单项都是独立的。
解决方案:
为了确保每次添加的都是一个全新的、独立的int[]对象,你需要将int[] Order = new int[2];的声明移动到do-while循环的内部,即在每次需要创建新订单项时再创建它。
正确放置数组声明:
public static void getOrder(ArrayList<int[]> ord) {
// ... 其他变量和输入逻辑 ...
do {
// ... 获取idConso 和 nbrConso 的用户输入 ...
// 每次循环迭代时,创建一个新的 int[] 对象
int[] currentOrder = new int[2];
currentOrder[0] = idConso;
currentOrder[1] = nbrConso;
boolean found = false;
for (int[] addedOrder : ord) {
if (addedOrder[0] == idConso) {
System.out.println("包含该产品,更新数量。");
addedOrder[1] += nbrConso; // 注意这里是+= nbrConso,而不是 = currentOrder[1]
found = true;
break;
}
}
if (!found) {
ord.add(currentOrder); // 添加这个新的订单数组
System.out.println("新产品,已添加。");
}
// ... 获取下一个用户输入 ...
} while (!UserInput.equalsIgnoreCase("V"));
// ... 打印结果 ...
}此外,原始代码中Integer [] OrderLine = new Integer [ord.size()]; OrderLine = ord.toArray(OrderLine);这两行代码在此上下文中是不必要且会导致运行时错误,因为ord存储的是int[]而不是Integer[]。这些代码可以直接删除。
结合上述所有改进,以下是getOrder方法的完整重构版本:
package testes;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;
public class test_EX_24V1 {
final static String NAMES[] =
{
"Spa reine 25 ","Bru plate 50","Bru pét 50","Pepsi","Spa orange",
"Schweppes Tonic","Schweppes Agr","Ice Tea","Ice Tea Pêche","Jus d'orange Looza", "Cécémel",
"Red Bull","Petit Expresso","Grand Expresso","Café décaféiné ","Lait Russe ","Thé et infusions",
"Irish Coffee ","French Coffee ","Cappuccino","Cécémel chaud","Passione Italiano","Amour Intense",
"Rhumba Caliente ","Irish Kisses ","Cuvée Trolls 25","Cuvee Trolls 50","Ambrasse-Temps 25","Ambrasse-Temps 50 ",
"Brasse-Temps Cerises 25","Brasse-Temps Cerises 50","La Blanche Ste Waudru 25","Blanche Ste Waudru 50",
"Brasse-Temps citr 25","Brasse-Temps citr 50","Spaghetti Bolo ","Tagl Carbonara","Penne poulet baslc ",
"Tagl American","Tagl saum"
};
public static String getUserIntOrSpecificInputV2(String msg, String expectedAnsw, int min, int max)
{
int intInput = 0;
String strAnsw = "";
Scanner sc= new Scanner (System.in); // 注意:Scanner应在方法外部或作为参数传递,避免每次调用都创建新实例
do {
System.out.println(msg);
if (sc.hasNextInt()) { //si saisie d’un entier
intInput = sc.nextInt();//récupération de la saisie au format entier
if (intInput >= min && intInput <= max) {
return Integer.toString(intInput);
} else {
System.out.println("La saisie doit être comprise entre " + min + " et " + max);
}
} else { //si saisie d'un texte par l’utilisateur
strAnsw = sc.next(); //récupération de la saisie au format chaine
if (strAnsw.length() == 1 && expectedAnsw.toUpperCase().contains(strAnsw.toUpperCase())){
return strAnsw.toUpperCase();
} else {
System.out.println ("Erreur de saisie : caractères autorisés " + expectedAnsw );
}
}
} while (true);
}
// checkTable 方法在当前场景下不再需要,因为我们直接操作ArrayList
// public static boolean checkTable( int[] table, int numberCheck ) { /* ... */ }
public static void getOrder ( ArrayList<int[]> ord ) {
// variables
String UserInput;
int idConso = 0, nbrConso = 0;
// 注意:Scanner sc = new Scanner(System.in); 应该在这里或更上层创建一次并传递
// 为了示例简化,暂时保留getUserIntOrSpecificInputV2内部创建Scanner,但实际开发中应优化
do {
UserInput = getUserIntOrSpecificInputV2("Entrez le N° de consommable "
+ "ou Q(Quitter) ", "Q", 1, NAMES.length);
if ( UserInput.equalsIgnoreCase( "Q" ) ) { // fin de programme
System.out.println( "Fin de Programme, Au Revoir" );
System.exit( -1 );
} else {
idConso = Integer.parseInt ( UserInput );
}
UserInput = getUserIntOrSpecificInputV2("Nombre de consommations pour " + NAMES [ idConso -1 ] +" ? /A(Annuler) /Q (Quitter)", "AQ", 1, 5000);
if ( UserInput.equalsIgnoreCase("Q") ) { // fermer l'application
System.out.println( "Fin de Programme, Au Revoir" );
System.exit(-1);
} else if ( UserInput.equalsIgnoreCase("A") ) {
// 如果用户选择取消,则跳过本次订单处理,直接进入下一轮输入
continue; // 跳过当前循环的剩余部分,进入下一次do-while迭代
}
nbrConso = Integer.parseInt ( UserInput );
// 每次循环迭代时,创建一个新的 int[] 对象
int[] currentOrder = new int[2];
currentOrder[0] = idConso;
currentOrder[1] = nbrConso;
boolean found = false;
for (int[] addedOrder : ord) { // 遍历已有的订单列表
if (addedOrder[0] == idConso) { // 检查产品ID是否已存在
System.out.println("产品ID " + idConso + " 已存在,更新数量。");
addedOrder[1] += nbrConso; // 累加数量
found = true;
break; // 找到并更新后,即可跳出循环
}
}
if (!found) { // 如果产品ID是新的
ord.add(currentOrder); // 将新的订单数组添加到列表中
System.out.println("产品ID " + idConso + " 是新产品,已添加。");
}
UserInput = getUserIntOrSpecificInputV2("Entrez le N° de consommable ou Q(Quitter) V (Valider le ticket) ", "QV",1, NAMES.length);
} while (!UserInput.equalsIgnoreCase("V")); // 循环直到用户选择“V”验证订单
// 打印最终的订单列表
System.out.println("\n--- 最终订单详情 ---");
for (int[] item : ord) {
System.out.print(Arrays.toString(item) + " (产品: " + NAMES[item[0]-1] + ", 数量: " + item[1] + ") ");
}
System.out.println();
System.out.println("订单项总数: " + ord.size());
}
public static void main(String[] args) {
// variables
ArrayList <int [] > orderList = new ArrayList<int[]>(); // 更改变量名以避免与局部变量混淆
getOrder(orderList);
}
}通过上述重构,我们解决了在ArrayList<int[]>中管理订单数据时常见的两个核心问题:
最佳实践建议:
虽然使用int[]来表示订单项在简单场景下是可行的,但在实际的、更复杂的应用中,强烈建议使用自定义类(POJO - Plain Old Java Object)来封装数据。例如,可以创建一个ProductOrder类:
public class ProductOrder {
private int id;
private int quantity;
public ProductOrder(int id, int quantity) {
this.id = id;
this.quantity = quantity;
}
// Getters and Setters
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public int getQuantity() { return quantity; }
public void setQuantity(int quantity) { this.quantity = quantity; }
public void addQuantity(int amount) { this.quantity += amount; }
// 重写 equals 和 hashCode 方法,以便 ArrayList.contains() 等集合操作能正确识别逻辑上的相等
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ProductOrder that = (ProductOrder) o;
return id == that.id; // 仅根据产品ID判断是否相等
}
@Override
public int hashCode() {
return Objects.hash(id); // 仅根据产品ID生成哈希码
}
@Override
public String toString() {
return "ProductOrder{" +
"id=" + id +
", quantity=" + quantity +
'}';
}
}使用自定义类ProductOrder后,ArrayList将变为ArrayList<ProductOrder>。通过重写equals()和hashCode()方法,ArrayList.contains()或使用HashMap等集合类将能更直观、高效地处理逻辑上的相等性(例如,仅根据id判断两个ProductOrder是否相同)。这不仅提高了代码的可读性和维护性,也使得集合操作更加符合直观预期。
以上就是Java ArrayList中管理一维数组:检测并更新重复元素的高效策略的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号