在 Java 中,ArrayList 是基于动态数组实现的列表,支持通过索引快速访问元素。计算目标元素前后的元素数量,本质上就是确定该元素在列表中的索引位置。只要拿到索引,前后数量就能直接通过索引值和列表总长度计算出来。不过实际开发中,列表里可能包含多个相同的元素,或者需要一次计算多个不同元素的统计结果,这时简单的 indexOf 调用可能会变得低效甚至不够准确。下面从基础方法开始,逐步讨论各种场景下的处理方式。

基础方法:使用 indexOf 和 lastIndexOf 定位索引
ArrayList 提供了两个常用的查找方法:indexOf(Object o) 和 lastIndexOf(Object o)。前者从列表头部开始向后扫描,返回第一次匹配元素的索引;后者从列表尾部开始向前扫描,返回最后一次匹配元素的索引。两个方法的时间复杂度都是 O(n),因为最坏情况下需要遍历整个列表。如果列表中不存在目标元素,两个方法都会返回 -1。
当目标元素在列表中只出现一次时,问题变得非常简单。假设 ArrayList 的长度为 size,目标元素的索引为 index,那么它前面的元素数量就是 index,后面的元素数量就是 size - index - 1。这里的 index 就是该元素在数组中的位置,因为 ArrayList 的索引从 0 开始。例如,一个长度为 5 的列表,目标元素在索引 2 的位置,那么前面有 2 个元素,后面有 5 - 2 - 1 = 2 个元素。
下面是一个使用 indexOf 计算唯一元素前后数量的示例代码。
import java.util.ArrayList;
import java.util.List;
public class UniqueElementCount {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");
list.add("cherry");
list.add("date");
list.add("elderberry");
String target = "cherry";
int index = list.indexOf(target);
int size = list.size();
if (index != -1) {
int before = index;
int after = size - index - 1;
System.out.println("目标元素前面的元素数量: " + before);
System.out.println("目标元素后面的元素数量: " + after);
} else {
System.out.println("目标元素不存在");
}
}
}
上面的代码先判断索引是否为 -1,避免出现负数索引。对于唯一元素,这种方法的效率已经足够,因为 indexOf 本身只需要一次线性扫描。但如果列表很大,并且需要频繁查询不同的目标元素,每次调用 indexOf 都会重新扫描列表,整体性能会下降,这正是后续需要优化的地方。
重复元素场景下的处理策略
实际数据中,目标元素很可能在列表里出现多次。此时 indexOf 只返回第一次出现的索引,lastIndexOf 只返回最后一次出现的索引。如果业务需要统计某个元素每次出现位置前后的元素数量,仅仅调用这两个方法是不够的。例如,列表中有一个字符串 "banana" 出现在索引 1 和索引 4 的位置,indexOf 返回 1,lastIndexOf 返回 4,中间的第二次出现会被忽略。
要处理重复元素,最直接的方式是遍历整个列表,记录所有匹配元素的索引。这样只需要一次遍历,就能得到目标元素的所有位置,然后对每个索引分别计算前后数量。与反复调用 indexOf 相比,这种方法避免了多次扫描,在一次遍历中完成全部统计,时间复杂度仍为 O(n),但常数项更小,且逻辑清晰。
下面是一个遍历列表并统计所有匹配位置前后数量的示例。
import java.util.ArrayList;
import java.util.List;
public class DuplicateElementCount {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");
list.add("cherry");
list.add("banana");
list.add("date");
list.add("banana");
String target = "banana";
int size = list.size();
List<Integer> positions = new ArrayList<>();
for (int i = 0; i < size; i++) {
if (list.get(i).equals(target)) {
positions.add(i);
}
}
if (positions.isEmpty()) {
System.out.println("目标元素不存在");
return;
}
for (int index : positions) {
int before = index;
int after = size - index - 1;
System.out.println("位置 " + index + " 前面的元素数量: " + before + ",后面的元素数量: " + after);
}
}
}
这段代码先收集所有匹配索引,再逐个输出前后数量。如果目标元素的出现次数较多,或者列表规模较大,这种一次遍历的方式比多次调用 indexOf 更高效。另外,如果业务只需要统计第一次或最后一次出现的前后数量,那么直接使用 indexOf 或 lastIndexOf 即可,遍历所有位置反而会造成不必要的开销。选择哪种方法取决于具体需求。
性能优化与注意事项
如果需要频繁查询多个不同元素在同一个 ArrayList 中的位置,每次都遍历列表显然不够高效。一个常见的优化手段是预先构建一个索引 Map,将每个元素映射到它所有出现位置的列表。这样,查询某个元素的位置时,只需要 O(1) 的时间从 Map 中取出索引列表,再根据索引列表计算前后数量。这种预处理方式特别适合列表数据相对静态、但查询操作非常频繁的场景。
构建索引 Map 的代码并不复杂,遍历一次列表,将索引按照元素值分组即可。下面给出示例。
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class IndexMapBuilder {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");
list.add("cherry");
list.add("banana");
list.add("date");
Map<String, List<Integer>> indexMap = new HashMap<>();
for (int i = 0; i < list.size(); i++) {
String key = list.get(i);
indexMap.computeIfAbsent(key, k -> new ArrayList<>()).add(i);
}
String target = "banana";
List<Integer> positions = indexMap.get(target);
if (positions != null) {
int size = list.size();
for (int index : positions) {
int before = index;
int after = size - index - 1;
System.out.println("位置 " + index + " 前面 " + before + " 个,后面 " + after + " 个");
}
} else {
System.out.println("目标元素不存在");
}
}
}
在构建索引 Map 之后,后续每次查询都不需要再扫描列表,大大提升了频繁查询的性能。不过需要注意,这种优化只适用于列表内容不会频繁变动的场景。如果列表会被修改,索引 Map 也需要同步更新,否则查询结果会出错。
另一个常见陷阱是遍历列表时修改列表。ArrayList 内部维护一个 modCount 变量,记录结构修改次数。如果使用迭代器或增强 for 循环遍历时直接调用 list.add 或 list.remove,会触发 ConcurrentModificationException。因此,在统计元素位置的过程中,不要同时修改列表。如果确实需要修改,建议先完成统计,再执行修改操作,或者使用迭代器的 remove 方法。
此外,对于超大型列表,遍历操作本身也存在性能压力。这时可以考虑将列表转换为数组,或者使用并行流来加速索引收集。但对于大多数业务场景,单次线性遍历已经足够快,过早优化反而会增加代码复杂度。优先保证代码可读性和正确性,在出现性能瓶颈时再考虑优化。
总结
计算 ArrayList 中目标元素前后的元素数量,核心在于获取目标元素的索引。对于唯一元素,直接使用 indexOf 或 lastIndexOf 就能高效定位;对于重复元素,需要遍历列表收集所有匹配位置,再逐一计算;对于高频查询场景,可以构建索引 Map 实现 O(1) 查询。实际开发中,应根据列表大小、元素是否重复以及查询频率来选择合适的方法。同时要注意避免在遍历过程中修改列表,防止异常。掌握这些技巧,就能在保证代码简洁的同时,高效可靠地完成元素前后数量的统计。
ArrayList索引元素前后计数indexOf方法修改时间:2026-10-03 03:49:20