在 Java 中比較一個字串與一個表達式中的多個值
1. 概述
在本教程中,我們將討論使用單一表達式在一組字串中尋找字串的各種方法。
假設有一個水果“Apple”
和一組水果“Mango”
、 “Papaya”
、 “Apple”
、 “Pineapple”
等。現在我們將探索各種方法來查看字符串“Apple”
是否存在於其中水果組。
2.問題介紹
在我們繼續討論單表達式解決方案的下一部分之前,讓我們先來看看帶有if
條件的實作:
boolean compareWithMultipleStringsUsingIf(String str, String ... strs) {
for(String s : strs) {
if (str.equals(s)) {
return true;
}
}
return false;
}
這是一個非常基本的實現,也許是所有實現中最受歡迎的。我們迭代String
數組,當String
str
與strs
中的任何一個元素相符時傳回true
。
讓我們看看這是如何運作的:
@Test
void givenStrings_whenCompareWithMultipleStringsUsingIf_thenSuccess() {
String presentString = "Apple";
String notPresentString = "Avocado";
assertTrue(compareWithMultipleStringsUsingIf(presentString, "Mango", "Papaya", "Pineapple", "Apple"));
assertFalse(compareWithMultipleStringsUsingIf(notPresentString, "Mango", "Papaya", "Pineapple", "Apple"));
}
我們在一組水果中發現了諸如“Apple”
和“Avacado”
之類的String
的存在。但這涉及多行程式碼。因此,讓我們看看接下來的部分,了解涉及單一表達式的解決方案。
3. 與Set
匹配
java.util.Set
具有contains()
方法,用於檢查集合中是否存在元素。因此,我們將使用java.util.Set
來處理我們的用例,並使用單一表達式:
boolean compareWithMultipleStringsUsingSet(String str, String ... strs) {
return Set.of(strs).contains(str);
}
使用單一表達式,我們初始化了一個Set
,然後使用contains()
方法來檢視Set
中是否存在str
。然而,我們無法使用Set.
讓我們測試compareWithMultipleStringsUsingSet()
方法:
@Test
void givenStrings_whenCompareWithMultipleStringsUsingSet_thenSuccess() {
String presentString = "Apple";
String notPresentString = "Avocado";
assertTrue(compareWithMultipleStringsUsingSet(presentString, "Mango", "Papaya", "Pineapple", "Apple"));
assertFalse(compareWithMultipleStringsUsingSet(notPresentString, "Mango", "Papaya", "Pineapple", "Apple"));
}
就像之前一樣,我們首先將“Apple”
傳遞給該方法,它傳回true
,當我們傳遞“Avocado”
時,它會傳回false
。因此,這效果很好。
4. 與List
匹配
與Set
類似, List
也有contains()
方法。因此,讓我們檢查一下使用List
實作:
boolean compareWithMultipleStringsUsingList(String str, String ... strs) {
return List.of(strs).contains(str);
}
沒有太大差別,我們用List
取代了Set
。
讓我們看看它的實際效果:
@Test
void givenStrings_whenCompareWithMultipleStringsUsingList_thenSuccess() {
String presentString = "Apple";
String notPresentString = "Avocado";
assertTrue(compareWithMultipleStringsUsingList(presentString, "Mango", "Papaya", "Pineapple", "Apple"));
assertFalse(compareWithMultipleStringsUsingList(notPresentString, "Mango", "Papaya", "Pineapple", "Apple"));
}
contains()
方法按預期工作並傳回正確的結果。
5. 與Stream
匹配
Stream API 鼓勵使用宣告式語句,因此它可以幫助我們實作單一表達式方法:
boolean compareWithMultipleStringsUsingStream(String str, String ... strs) {
return Arrays.stream(strs).anyMatch(str::equals);
}
在單一表達式中,我們將String
陣列轉換為Stream
,然後使用anyMatch()
方法。方法anyMatch()
是Stream
管道中的終端操作。 Stream
中的每個元素都會與String
str.
但是, anyMatch()
方法傳回第一個符合項目。
讓我們看看該方法的實際效果:
@Test
void givenStrings_whenCompareWithMultipleStringsUsingStream_thenSuccess() {
String presentString = "Apple";
String notPresentString = "Avocado";
assertTrue(compareWithMultipleStringsUsingStream(presentString, "Mango", "Papaya", "Pineapple", "Apple"));
assertFalse(compareWithMultipleStringsUsingStream(notPresentString, "Mango", "Papaya", "Pineapple", "Apple"));
}
該方法按預期工作,以“Apple”
和“Avocado”
作為第一個參數,分別傳回true
和false
。
讓我們看看使用Stream
不區分大小寫的版本:
boolean compareCaseInsensitiveWithMultipleStringsUsingStream(String str, String ... strs) {
return Arrays.stream(strs).anyMatch(str::equalsIgnoreCase);
}
與早期版本不同,我們必須呼叫equalsIgnoreCase()
方法作為anyMatch()
的謂詞。
我們現在可以看看該方法的實際應用:
@Test
void givenStrings_whenCompareCaseInsensitiveWithMultipleStringsUsingStream_thenSuccess() {
String presentString = "APPLE";
String notPresentString = "AVOCADO";
assertTrue(compareCaseInsensitiveWithMultipleStringsUsingStream(presentString, "Mango", "Papaya", "Pineapple", "Apple"));
assertFalse(compareCaseInsensitiveWithMultipleStringsUsingStream(notPresentString, "Mango", "Papaya", "Pineapple", "Apple"));
}
這次它可以在“Mango”
、 “Papaya”
、 “Pineapple”
和“Apple”中找到“APPLE”
“Apple”.
6. 與StringUtils
匹配
在我們使用commons-lang3
庫中的StringUtils
類別之前,我們首先使用其Maven 依賴項更新pom.xml
:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.13.0</version>
</dependency>
現在,讓我們使用StringUtils
類別:
boolean compareWithMultipleStringsUsingStringUtils(String str, String ... strs) {
return StringUtils.equalsAny(str, strs);
}
StringUtils
中的equalsAny()
方法有助於在單一表達式中實作我們的用例。
讓我們看看該方法的實際效果:
@Test
void givenStrings_whenCompareWithMultipleStringsUsingStringUtils_thenSuccess() {
String presentString = "Apple";
String notPresentString = "Avocado";
assertTrue(compareWithMultipleStringsUsingStringUtils(presentString, "Mango", "Papaya", "Pineapple", "Apple"));
assertFalse(compareWithMultipleStringsUsingStringUtils(notPresentString, "Mango", "Papaya", "Pineapple", "Apple"));
}
這也按預期工作,當“Apple”
和“Mango”
傳遞給該方法時分別傳回true
和false
。
我們也來看看該方法的不區分大小寫的版本:
boolean compareCaseInsensitiveWithMultipleStringsUsingStringUtils(String str, String ... strs) {
return StringUtils.equalsAnyIgnoreCase(str, strs);
}
我們使用StringUtils
的equalsAny()
equalsAnyIgnoreCase()
方法。
讓我們看看該方法是如何運作的:
@Test
void givenStrings_whenCompareCaseInsensitiveWithMultipleStringsUsingStringUtils_thenSuccess() {
String presentString = "APPLE";
String notPresentString = "AVOCADO";
assertTrue(compareCaseInsensitiveWithMultipleStringsUsingStringUtils(presentString, "Mango", "Papaya", "Pineapple", "Apple"));
assertFalse(compareCaseInsensitiveWithMultipleStringsUsingStringUtils(notPresentString, "Mango", "Papaya", "Pineapple", "Apple"));
}
我們可以成功地在水果組中找到String
“APPLE”
。
7. 與ArrayUtils
匹配
此外,我們將看到來自同一個commons-lang
庫的另一個類別ArrayUtils
:
boolean compareWithMultipleStringsUsingArrayUtils(String str, String ... strs) {
return ArrayUtils.contains(strs, str);
}
ArrayUtils
是一個實用程式類,有助於檢查物件陣列中是否存在某個元素。因此,我們利用它來幫助我們實作涉及String
用例。不幸的是, ArrayUtils
並沒有提供任何以不區分大小寫的方式尋找String
物件的方法。因此,我們不能對其進行單一表達式實作。
讓我們看看compareWithAnyUsingArrayUtils()
方法是如何運作的:
@Test
void givenStrings_whenCompareWithMultipleStringsUsingArrayUtils_thenSuccess() {
String presentString = "Apple";
String notPresentString = "Avocado";
assertTrue(compareWithMultipleStringsUsingArrayUtils(presentString, "Mango", "Papaya", "Pineapple", "Apple"));
assertFalse(compareWithMultipleStringsUsingArrayUtils(notPresentString, "Mango", "Papaya", "Pineapple", "Apple"));
}
毫不奇怪,它也有效。
8. 與正規表示式匹配
正規表示式有助於查找模式,因此我們將其用於我們的用例:
boolean compareWithMultipleStringsUsingRegularExpression(String str, String ... strs) {
return str.matches(String.join("|", strs));
}
String.join()
方法建立一個以垂直線分隔的String
列表,例如Mango|Papaya|Pineapple|Apple
用作正規表示式模式。在單一表達式中,我們使用String
s 陣列建立正規表示式模式,然後使用matches()
方法檢查String
str
是否具有該模式。
是時候看看方法的實際效果了:
@Test
void givenStrings_whenCompareWithMultipleStringsUsingRegularExpression_thenSuccess() {
String presentString = "Apple";
String notPresentString = "Avocado";
assertTrue(compareWithMultipleStringsUsingRegularExpression(presentString, "Mango", "Papaya", "Pineapple", "Apple"));
assertFalse(compareWithMultipleStringsUsingRegularExpression(notPresentString, "Mango", "Papaya", "Pineapple", "Apple"));
}
此方法對於參數“Mango”
傳回true
,對於“Avocado”
false
。因此,我們可以說它也有效。但是,正規表示式始終是效能密集的,因此最好避免使用它們。
現在,讓我們來看看不區分大小寫的實作:
boolean compareCaseInsensitiveWithMultipleStringsUsingRegularExpression(String str, String ... strs) {
return str.matches("(?i)" + String.join("|", strs));
}
我們只需透過在正規表示式前面加上(?i)
來修改正規表示式即可進行不區分大小寫的模式比對。
讓我們來看看該方法的實際效果:
@Test
void givenStrings_whenCompareCaseInsensitiveUsingRegularExpression_thenSuccess() {
String presentString = "APPLE";
String notPresentString = "AVOCADO";
assertTrue(compareCaseInsensitiveWithMultipleStringsUsingRegularExpression(presentString, "Mango", "Papaya", "Pineapple", "Apple"));
assertFalse(compareCaseInsensitiveWithMultipleStringsUsingRegularExpression(notPresentString, "Mango", "Papaya", "Pineapple", "Apple"));
}
透過使用現在的方法,我們也可以在提供的水果組中找到“APPLE”
。
9. 基準
讓我們使用 Java Microbenchmark Harness (JMH) 計算每個單一表達式方法的平均執行時間。
讓我們來看看為執行基準測試而配置的類別:
@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 2)
@Measurement(iterations = 5)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Fork(value = 1)
public class CompareAnyBenchmark {
private final String[] groupOfFruits = {"Apple", "Mango", "Dragon Fruit", "Water Melon", "Avocado", "Guava", "Orange"};
private final String fruit = "Apple";
@Benchmark
public boolean compareWithMultipleStringsUsingStringUtils() {
return StringUtils.equalsAny(fruit, groupOfFruits);
}
@Benchmark
public boolean compareCaseInsensitiveWithMultipleStringsUsingStringUtils() {
return StringUtils.equalsAnyIgnoreCase(fruit, groupOfFruits);
}
//Other benchmark methods...
public static void main(String[] args) throws Exception {
Options options = new OptionsBuilder().include(CompareAnyBenchmark.class.getSimpleName())
.threads(1)
.shouldFailOnError(true)
.shouldDoGC(true)
.jvmArgs("-server")
.build();
new Runner(options).run();
}
}
類別層級的註釋設定了基準來測量每種方法運行五次所需的平均時間(以奈秒為單位)。最後, main()
方法用於運行基準測試。
現在讓我們來看看到目前為止我們討論的每種方法所花費的平均執行時間:
方法名稱 | 平均。時間 | 誤差(±) | 單元 |
---|---|---|---|
compareWithMultipleStringsUsingArrayUtils() |
1.150 | 0.031 | ns/操作 |
compareWithMultipleStringsUsingRegularExpression() |
1175.809 | 177.940 | ns/操作 |
compareWithMultipleStringsUsingSet() |
96.961 | 11.943 | ns/操作 |
`compareWithMultipleStringsUsingList() | |||
` | 28.718 | 1.612 | ns/操作 |
compareWithMultipleStringsUsingStream() |
47.266 | 3.968 | ns/操作 |
compareWithMultipleStringsUsingStringUtils |
1.507 | 0.040 | ns/操作 |
compareCaseInsensitiveWithMultipleStringsUsingRegularExpression() |
1803.497 | 645.104 | ns/操作 |
compareCaseInsensitiveWithMultipleStringsUsingStream() |
63.079 | 56.509 | ns/操作 |
compareCaseInsensitiveWithMultipleStringsUsingStringUtils() |
1.521 | 0.077 | ns/操作 |
使用正規表示式的方法compareCaseInsensitiveWithMultipleStringsUsingRegularExpression()
和compareWithMultipleStringsUsingRegularExpression()
執行時間最長。另一方面, compareWithMultipleStringsUsingArrayUtils
(), and compareWithMultipleStringsUsingStringUtils()
方法的執行時間最少。
在不考慮任何外部函式庫的情況下, compareWithMultipleStringsUsingStream
(),
和compareCaseInsensitiveWithMultipleStringsUsingStream
()
方法得分最高。此外,對於不區分大小寫的搜索,效能也沒有太大變化。
10. 結論
在本文中,我們探索了在一組String
中尋找String
是否存在的各種方法。對於java.util.Set
、 java.util.List
、 java.util.Stream
和正規表示式,我們沒有使用 JDK 以外的任何外部函式庫。因此,建議使用它們,而不是使用像commons-lang
這樣的外部函式庫。而且, List
實作是JDK函式庫中的最佳選擇。
與往常一樣,程式碼範例可以在 GitHub 上找到。