「如何判斷陣列/集合是否包含某元素」是高頻面試題與實戰需求。本文系統對比 **5 大方案**,從時間複雜度、空間複雜度、適用條件、實測效能給出選型指南。
String[] arr = {"aaa", "solo", "king"};
Arrays.sort(arr); // 必須先排序!
int index = Arrays.binarySearch(arr, "king");
boolean exists = index >= 0; // 找到返回索引 >=0,否則返回 (-(插入點) - 1)
String[] arr = {"aaa", "solo", "king"};
String arrStr = Arrays.toString(arr); // "[aaa, solo, king]"
Pattern p = Pattern.compile("\\bking\\b"); // 詞邊界,避免部分匹配
boolean exists = p.matcher(arrStr).find();
public static boolean containsLinear(String[] arr, String target) {
if (arr == null) return false;
for (String s : arr) {
if (target.equals(s)) return true; // null 安全
}
return false;
}
String[] arr = {"aaa", "solo", "king"};
Set set = new HashSet<>(Arrays.asList(arr)); // O(n) 建立
boolean exists = set.contains("king"); // O(1) 查找
boolean exists = Arrays.stream(arr).anyMatch("king"::equals);
// 或基本型別
int[] nums = {1,2,3};
boolean exists = IntStream.of(nums).anyMatch(x -> x == 2);
Collection coll = new ArrayList<>(Arrays.asList("aaa", "solo", "king"));
boolean exists = coll.contains("king"); // O(n) List,O(1) HashSet
Collection template = new HashSet<>(Arrays.asList("aaa", "solo", "king"));
Collection target = new ArrayList<>(Arrays.asList("aaa", "bbb", "ccc"));
// 存在於模板中的元素
Collection exist = new ArrayList<>(target);
exist.retainAll(template); // ["aaa"]
// 不存在於模板中的元素
Collection notExist = new ArrayList<>(target);
notExist.removeAll(template); // ["bbb", "ccc"]
Map> partitioned = target.stream()
.collect(Collectors.partitioningBy(template::contains));
// partitioned.get(true) -> 存在
// partitioned.get(false) -> 不存在
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Thread)
public class ContainsBenchmark {
@Param({"10", "100", "1000", "10000"})
int size;
String[] arr;
String target;
Set set;
@Setup
public void setup() {
arr = new String[size];
for (int i = 0; i < size; i++) arr[i] = "item" + i;
target = "item" + (size - 1); // 最壞情況:最後一個
set = new HashSet<>(Arrays.asList(arr));
Arrays.sort(arr); // 為 binarySearch 準備
}
@Benchmark public boolean linear() { return containsLinear(arr, target); }
@Benchmark public boolean binary() { return Arrays.binarySearch(arr, target) >= 0; }
@Benchmark public boolean hashSet() { return set.contains(target); }
@Benchmark public boolean stream() { return Arrays.stream(arr).anyMatch(target::equals); }
/* 典型結果(納秒):
size=10 linear: 30 binary: 40 hashSet: 25 stream: 50
size=100 linear: 80 binary: 60 hashSet: 25 stream: 120
size=1000 linear: 600 binary: 80 hashSet: 25 stream: 900
size=10000 linear: 5000 binary: 100 hashSet: 25 stream: 8000
*/
}
查找頻率?
├── 單次/低頻
│ ├── 陣列小(<50) → 線性循環 / Stream.anyMatch
│ ├── 陣列大、已排序 → binarySearch
│ └── 陣列大、未排序 → 線性循環(排序開銷 > 線性掃描)
└── 高頻/多次
├── 可預先建立 Set → HashSet.contains(最優)
├── 不可修改原陣列 → 建立 Set 緩存 / binarySearch(需排序)
└── 基本型別陣列 → IntStream / 線性循環 / 自建 IntSet
**「單次查找用循環,多次查找建 Set,已排序用二分,基本型別用 Stream/循環」**。理解各方案時間空間複雜度、前提條件、JIT 優化特性,才能在實際業務中做出最優選擇。避免盲目使用 `Arrays.asList(arr).contains()`(基本型別陣列失效、物件陣列 O(n))、正則匹配(脆弱低效)等反模式。