问:
我有一个具有如下值的 String[]:
public static final String[] VALUES = new String[] {"AB","BC","CD","AE"};
给定 String s,是否有测试 VALUES 是否包含 s 的好方法?
答1:
huntsbot.com高效搞钱,一站式跟进超10+任务平台外包需求
Arrays.asList(yourArray).contains(yourValue)
警告:这不适用于基元数组(请参阅注释)。
从 java-8 开始,您现在可以使用 Streams。
String[] values = {"AB","BC","CD","AE"};
boolean contains = Arrays.stream(values).anyMatch("s"::equals);
要检查 int、double 或 long 的数组是否包含值,请分别使用 IntStream、DoubleStream 或 LongStream。
例子
int[] a = {1,2,3,4};
boolean contains = IntStream.of(a).anyMatch(x -> x == 4);
我有点好奇 this 与 Arrays 类中的搜索函数与迭代数组并使用 equals() 函数或 == 用于基元的性能。
你不会损失太多,因为 asList() 返回一个 ArrayList,它的核心是一个数组。构造函数只会改变一个引用,所以在那里不需要做太多的工作。并且 contains()/indexOf() 将迭代并使用 equals()。不过,对于原语,您最好自己编写代码。对于字符串或其他类,差异不会很明显。
奇怪的是,NetBeans 声称“int[] 假期”的“Arrays.asList(holidays)”返回一个“list”,而不是“list”。它只包含一个元素。这意味着 Contains 不起作用,因为它只有一个元素;整数数组。
Nyerguds:确实,这不适用于原语。在 java 中,原始类型不能是泛型的。 asList 被声明为 List asList(T...)。当您将 int[] 传递给它时,编译器会推断 T=int[] 因为它不能推断 T=int,因为原语不能是通用的。
@Joey 只是一个旁注,它是一个 ArrayList,但不是您期望的 java.util.ArrayList,真正返回的类是:java.util.Arrays.ArrayList 定义为:public class java.util.Arrays {private static class ArrayList ... {}}。
答2:
打造属于自己的副业,开启自由职业之旅,从huntsbot.com开始!
Java SE 9 的简明更新
参考数组不好。对于这种情况,我们是在一组之后。从 Java SE 9 开始,我们有了 Set.of。
private static final Set VALUES = Set.of(
"AB","BC","CD","AE"
);
“给定 String s,有没有一种测试 VALUES 是否包含 s 的好方法?”
VALUES.contains(s)
O(1)。
正确的类型,不可变的,O(1) 且简洁。美丽的。*
原始答案详情
只是为了清除代码开始。我们有(更正):
public static final String[] VALUES = new String[] {"AB","BC","CD","AE"};
这是一个可变的静态变量,FindBugs 会告诉你它非常顽皮。不要修改静态,也不允许其他代码也这样做。至少,该字段应该是私有的:
private static final String[] VALUES = new String[] {"AB","BC","CD","AE"};
(注意,您实际上可以删除 new String[]; 位。)
参考数组仍然很糟糕,我们想要一个集合:
private static final Set VALUES = new HashSet(Arrays.asList(
new String[] {"AB","BC","CD","AE"}
));
(偏执狂的人,例如我自己,如果将其包含在 Collections.unmodifiableSet 中,可能会感到更自在——甚至可以将其公开。)
(*关于品牌的更多信息,可以预见,集合 API 仍然缺少不可变集合类型,而且语法仍然过于冗长,不符合我的口味。)
除了首先创建集合是 O(N) :)
如果它是静态的,它可能会被使用很多次。因此,与大量线性搜索的成本相比,初始化集合所花费的时间很有可能非常小。
@TomHawtin-tackline 你为什么说“特别是在这里我们想要一套”?在这种情况下,Set(HashSet)有什么优势?为什么“参考数组”不好(“参考数组”是指由调用 Arrays.asList 生成的数组支持的 ArrayList)?
@nmr A TreeSet 将是 O(log n)。 HashSet 被缩放,使得桶中元素的平均数量大致恒定。至少对于最多 2^30 的数组。例如,可能会受到 big-O 分析忽略的硬件缓存的影响。还假设哈希函数有效工作。
这并不能回答有关数组的问题。您只是说“不要使用数组”,这不是解决方案。此外,您只是说“X 不好”,但没有解释为什么这对答案总是不好的。
答3:
huntsbot.com全球7大洲远程工作机会,探索不一样的工作方式
您可以使用 Apache Commons Lang 中的 ArrayUtils.contains
public static boolean contains(Object[] array, Object objectToFind)
请注意,如果传递的数组是 null,则此方法返回 false。
还有一些方法可用于各种原始数组。
例子:
String[] fieldsToInclude = { "id", "name", "location" };
if ( ArrayUtils.contains( fieldsToInclude, "id" ) ) {
// Do some stuff.
}
@max4ever 我同意,但这仍然比“滚动你自己的”更好,并且比原始的 Java 方式更容易阅读。
包:org.apache.commons.lang.ArrayUtils
@max4ever 有时您已经包含了这个库(出于其他原因),这是一个完全有效的答案。我一直在寻找这个,我已经依赖于 Apache Commons Lang。感谢您的回答。
或者您可以只复制该方法(以及如果有的话)。
@max4ever 大多数 android 应用程序都被 Proguard 最小化,只将你需要的类和函数放入你的应用程序中。这使它等于滚动您自己的,或复制 apache 事物的源代码。任何不使用最小化的人都不需要抱怨 700kb 或 78kb :)
答4:
一个优秀的自由职业者,应该有对需求敏感和精准需求捕获的能力,而huntsbot.com提供了这个机会
只需简单地手动实现它:
public static boolean contains(final T[] array, final T v) {
for (final T e : array)
if (e == v || v != null && v.equals(e))
return true;
return false;
}
改进:
v != null 条件在方法内是常量。在方法调用期间,它始终评估为相同的布尔值。因此,如果输入 array 很大,那么只评估一次这个条件会更有效,我们可以根据结果在 for 循环内使用简化/更快的条件。改进的 contains() 方法:
public static boolean contains2(final T[] array, final T v) {
if (v == null) {
for (final T e : array)
if (e == null)
return true;
}
else {
for (final T e : array)
if (e == v || v.equals(e))
return true;
}
return false;
}
@Phoexo这个解决方案显然更快,因为接受的答案将数组包装成一个列表,并在该列表上调用 contains() 方法,而我的解决方案基本上只做 contains() 会做的事情。
@AlastorMoody e==v 进行非常快的引用相等检查。如果相同的对象(通过引用相同)在数组中,将更快地找到它。如果不是同一个实例,它仍然可能与 equals() 方法声明的相同,如果引用不同,则检查此内容。
为什么这个函数不是 Java 的一部分?难怪人们说 Java 太臃肿了……看看上面的所有答案,当你只需要一个 for 循环时,它们使用了一堆库。现在的孩子们!
@phreakhead 它是 Java 的一部分,请参阅 Collection.contains(Object)
@icza 如果您查看 Arrays 和 ArrayList 的来源,事实证明这不一定比使用 Arrays.asList(...).contains(...) 的版本快。创建 ArrayList 的开销非常小,并且 ArrayList.contains() 使用比上面所示的循环(JDK 7)更智能的循环(实际上它使用两个不同的循环)。
答5:
huntsbot.com精选全球7大洲远程工作机会,涵盖各领域,帮助想要远程工作的数字游民们能更精准、更高效的找到对方。
Four Different Ways to Check If an Array Contains a Value
使用列表: public static boolean useList(String[] arr, String targetValue) { return Arrays.asList(arr).contains(targetValue); } 使用集合: public static boolean useSet(String[] arr, String targetValue) { Set set = new HashSet(Arrays.asList(arr));返回 set.contains(targetValue); } 使用一个简单的循环: public static boolean useLoop(String[] arr, String targetValue) { for (String s: arr) { if (s.equals(targetValue)) return true; } 返回假;使用 Arrays.binarySearch():下面的代码是错误的,为了完整起见,在此列出。 binarySearch() 只能用于已排序的数组。你会发现下面的结果很奇怪。这是对数组进行排序时的最佳选择。 public static boolean binarySearch(String[] arr, String targetValue) { return Arrays.binarySearch(arr, targetValue) >= 0; }
快速示例:
String testValue="test";
String newValueNotInList="newValue";
String[] valueArray = { "this", "is", "java" , "test" };
Arrays.asList(valueArray).contains(testValue); // returns true
Arrays.asList(valueArray).contains(newValueNotInList); // returns false
您的二进制搜索示例应返回 > 0;
为什么?我认为它应该返回 > -1,因为 0 表示它包含在数组的头部。
第一个带有 (a >= 0) 的变体是正确的,只需检查 the docs,他们说“请注意,这保证了当且仅当找到密钥时返回值将是 >= 0”。
为什么适用于 String 而不是 int? static boolean exists(int[] ints, int k) { return Arrays.asList(ints).contains(k); }
答6:
huntsbot.com高效搞钱,一站式跟进超10+任务平台外包需求
如果数组未排序,则必须遍历所有内容并在每个上调用 equals。
如果数组已排序,则可以进行二分搜索,Arrays 类中有一个。
一般来说,如果您要进行大量的成员资格检查,您可能希望将所有内容存储在 Set 中,而不是存储在数组中。
另外,就像我在回答中所说的那样,如果您使用 Arrays 类,您可以对数组进行排序,然后对新排序的数组执行二进制搜索。
@Thomas:我同意。或者您可以将所有内容添加到 TreeSet 中;相同的复杂性。如果它不改变,我会使用数组(也许可以节省一点内存位置,因为引用是连续定位的,尽管字符串不是)。如果这会随着时间的推移而改变,我会使用这套设备。
答7:
huntsbot.com高效搞钱,一站式跟进超10+任务平台外包需求
对于它的价值,我进行了一个测试,比较了 3 个速度建议。我生成了随机整数,将它们转换为字符串并将它们添加到数组中。然后,我搜索了可能的最高数字/字符串,这对于 asList().contains() 来说是最坏的情况。
使用 10K 数组大小时,结果为:
Sort & Search : 15
Binary Search : 0
asList.contains : 0
使用 100K 数组时,结果为:
Sort & Search : 156
Binary Search : 0
asList.contains : 32
因此,如果数组是按排序顺序创建的,则二进制搜索是最快的,否则 asList().contains 将是要走的路。如果您有很多搜索,那么可能值得对数组进行排序,以便您可以使用二进制搜索。这完全取决于您的应用程序。
我认为这些是大多数人所期望的结果。这是测试代码:
import java.util.*;
public class Test {
public static void main(String args[]) {
long start = 0;
int size = 100000;
String[] strings = new String[size];
Random random = new Random();
for (int i = 0; i < size; i++)
strings[i] = "" + random.nextInt(size);
start = System.currentTimeMillis();
Arrays.sort(strings);
System.out.println(Arrays.binarySearch(strings, "" + (size - 1)));
System.out.println("Sort & Search : "
+ (System.currentTimeMillis() - start));
start = System.currentTimeMillis();
System.out.println(Arrays.binarySearch(strings, "" + (size - 1)));
System.out.println("Search : "
+ (System.currentTimeMillis() - start));
start = System.currentTimeMillis();
System.out.println(Arrays.asList(strings).contains("" + (size - 1)));
System.out.println("Contains : "
+ (System.currentTimeMillis() - start));
}
}
我不明白这段代码。您对数组“字符串”进行排序,并在对 binarySearch 的两次调用中使用相同的(排序的)数组。除了 HotSpot 运行时优化之外,它还能显示什么?与 asList.contains 调用相同。您从排序后的数组创建一个列表,然后在其上包含最高值。当然,这需要时间。这个测试的意义是什么?更不用说编写不当的微基准测试了
此外,由于二分查找只能应用于已排序的集合,因此排序和搜索是使用二分查找的唯一可能方式。
由于许多其他原因,排序可能已经完成,例如,它可以在 init 上排序并且永远不会更改。可以单独测试搜索时间。然而,这失败的地方在于它是微基准测试的一个不太出色的例子。众所周知,微基准测试在 Java 中很难正确执行,例如应该包括在运行实际测试之前执行足以获得热点优化的测试代码,更不用说使用计时器运行实际测试代码超过一次。 Example pitfalls
该测试存在缺陷,因为它在同一个 JVM 实例中运行所有 3 个测试。后面的测试可以从早期的预热缓存、JIT 等中受益
这个测试实际上是完全无关的。排序和搜索是线性 (n*log(n)) 复杂度,二进制搜索是对数,ArrayUtils.contains 显然是线性的。比较这些解决方案是没有用的,因为它们属于完全不同的复杂性类别。
答8:
huntsbot.com高效搞钱,一站式跟进超10+任务平台外包需求
您可以使用 Arrays.asList 方法以类似的方式立即将其初始化为 List ,而不是使用快速数组初始化语法,例如:
public static final List STRINGS = Arrays.asList("firstString", "secondString" ...., "lastString");
然后你可以做(如上):
STRINGS.contains("the string you want to find");
答9:
huntsbot.com洞察每一个产品背后的需求与收益,从而捕获灵感
使用 Java 8,您可以创建一个流并检查流中是否有任何条目与 “s” 匹配:
String[] values = {"AB","BC","CD","AE"};
boolean sInArray = Arrays.stream(values).anyMatch("s"::equals);
或作为通用方法:
public static boolean arrayContains(T[] array, T value) {
return Arrays.stream(array).anyMatch(value::equals);
}
还值得注意的是原始专业化。
还要补充一点,anyMatch JavaDoc 声明它是 "...May not evaluate the predicate on all elements if not necessary for determining the result.",因此它可能不需要在找到匹配项后继续处理。
答10:
huntsbot.com – 高效赚钱,自由工作
您可以使用 Arrays class 对值执行二分搜索。如果您的数组未排序,则必须使用同一类中的排序函数对数组进行排序,然后搜索它。
您可以使用同一类中的排序函数来完成此操作...我应该将其添加到我的答案中。
我认为可能会比 asList().contains() 方法花费更多。除非您需要经常进行该检查(但如果它只是一个可以开始排序的静态值列表,公平地说)。
真的。关于哪个最有效,有很多变数。不过有选择也不错。
在此处执行此操作的一些代码:stackoverflow.com/a/48242328/9131078
为搜索目的对整个数组进行排序是昂贵的。我们可以为线性搜索本身使用相同的 CPU 时间。我更喜欢对预先按排序顺序构建的集合进行二进制搜索。
答11:
huntsbot.com – 高效赚钱,自由工作
ObStupidAnswer(但我认为这里有一个教训):
enum Values {
AB, BC, CD, AE
}
try {
Values.valueOf(s);
return true;
} catch (IllegalArgumentException exc) {
return false;
}
异常抛出显然很重,但如果它有效,这将是一种测试值的新颖方法。缺点是必须事先定义枚举。
huntsbot.com聚合了超过10+全球外包任务平台的外包需求,寻找外包任务与机会变的简单与高效。