我最近被要求为一份工作编写3个测试程序。它们只使用核心Java API和我选择的任何测试框架编写。应在适当的时候实施单元测试。
虽然我根本没有收到任何反馈,但我认为他们不喜欢我的解决方案(否则我会听到他们的意见),所以我决定在这里展示我的程序,并询问这个实现是否可以被认为是好的,如果不是,为什么呢?
为了避免混淆,我现在只问第一个。
实现一个找到的函数 另一个更大的数组中的数组它 应该接受两个数组作为参数 它将返回该索引 第一个数组所在的第二个数组 首先完全发生。例如, findArray([2,3,7,1,20],[7,1])应该 返回2.
我没有尝试找到任何现有的解决方案,而是想自己做。
可能的原因: 1.应该是静态的。 2.应该使用行注释而不是块注释。 3.没有先检查空值(我知道,发现太晚了)。 4.?
更新:
提出了很多理由,我很难选择一个答案,因为很多答案都有很好的解决方案。正如@adietrich所提到的,我倾向于相信他们希望我展示核心API的知识(他们甚至要求编写函数,而不是编写算法)。
我认为确保工作的最佳方法是提供尽可能多的解决方案,包括: 1.使用Collections.indexOfSubList()方法实现,以表明我知道核心集合API。 2.使用强力方法实施,但提供更优雅的解决方案。 3.使用搜索算法实现,例如Boyer-Moore。 4.使用System.arraycopy()和Arrays.equal()的组合实现。然而,就性能而言,它不是最佳解决方案,它将显示我对标准数组例程的了解。
谢谢大家的回答!
END OF UPDATE。
这是我写的:
实际计划:
package com.example.common.utils;
/**
* This class contains functions for array manipulations.
*
* @author Roman
*
*/
public class ArrayUtils {
/**
* Finds a sub array in a large array
*
* @param largeArray
* @param subArray
* @return index of sub array
*/
public int findArray(int[] largeArray, int[] subArray) {
/* If any of the arrays is empty then not found */
if (largeArray.length == 0 || subArray.length == 0) {
return -1;
}
/* If subarray is larger than large array then not found */
if (subArray.length > largeArray.length) {
return -1;
}
for (int i = 0; i < largeArray.length; i++) {
/* Check if the next element of large array is the same as the first element of subarray */
if (largeArray[i] == subArray[0]) {
boolean subArrayFound = true;
for (int j = 0; j < subArray.length; j++) {
/* If outside of large array or elements not equal then leave the loop */
if (largeArray.length <= i+j || subArray[j] != largeArray[i+j]) {
subArrayFound = false;
break;
}
}
/* Sub array found - return its index */
if (subArrayFound) {
return i;
}
}
}
/* Return default value */
return -1;
}
}
测试代码:
package com.example.common.utils;
import com.example.common.utils.ArrayUtils;
import junit.framework.TestCase;
public class ArrayUtilsTest extends TestCase {
private ArrayUtils arrayUtils = new ArrayUtils();
public void testFindArrayDoesntExist() {
int[] largeArray = {1,2,3,4,5,6,7};
int[] subArray = {8,9,10};
int expected = -1;
int actual = arrayUtils.findArray(largeArray, subArray);
assertEquals(expected, actual);
}
public void testFindArrayExistSimple() {
int[] largeArray = {1,2,3,4,5,6,7};
int[] subArray = {3,4,5};
int expected = 2;
int actual = arrayUtils.findArray(largeArray, subArray);
assertEquals(expected, actual);
}
public void testFindArrayExistFirstPosition() {
int[] largeArray = {1,2,3,4,5,6,7};
int[] subArray = {1,2,3};
int expected = 0;
int actual = arrayUtils.findArray(largeArray, subArray);
assertEquals(expected, actual);
}
public void testFindArrayExistLastPosition() {
int[] largeArray = {1,2,3,4,5,6,7};
int[] subArray = {5,6,7};
int expected = 4;
int actual = arrayUtils.findArray(largeArray, subArray);
assertEquals(expected, actual);
}
public void testFindArrayDoesntExistPartiallyEqual() {
int[] largeArray = {1,2,3,4,5,6,7};
int[] subArray = {6,7,8};
int expected = -1;
int actual = arrayUtils.findArray(largeArray, subArray);
assertEquals(expected, actual);
}
public void testFindArrayExistPartiallyEqual() {
int[] largeArray = {1,2,3,1,2,3,4,5,6,7};
int[] subArray = {1,2,3,4};
int expected = 3;
int actual = arrayUtils.findArray(largeArray, subArray);
assertEquals(expected, actual);
}
public void testFindArraySubArrayEmpty() {
int[] largeArray = {1,2,3,4,5,6,7};
int[] subArray = {};
int expected = -1;
int actual = arrayUtils.findArray(largeArray, subArray);
assertEquals(expected, actual);
}
public void testFindArraySubArrayLargerThanArray() {
int[] largeArray = {1,2,3,4,5,6,7};
int[] subArray = {4,5,6,7,8,9,10,11};
int expected = -1;
int actual = arrayUtils.findArray(largeArray, subArray);
assertEquals(expected, actual);
}
public void testFindArrayExistsVeryComplex() {
int[] largeArray = {1234, 56, -345, 789, 23456, 6745};
int[] subArray = {56, -345, 789};
int expected = 1;
int actual = arrayUtils.findArray(largeArray, subArray);
assertEquals(expected, actual);
}
}
答案 0 :(得分:36)
“仅使用核心Java API”的要求也意味着他们想要看看你是否会重新发明轮子。因此,除了您自己的实现之外,您还可以提供单行解决方案,以确保安全:
public static int findArray(Integer[] array, Integer[] subArray)
{
return Collections.indexOfSubList(Arrays.asList(array), Arrays.asList(subArray));
}
指出给定的示例包含无效的数组文字可能是也可能不是一个好主意。
答案 1 :(得分:4)
好吧,我的头顶:
是的,应该是静态的。
一家抱怨此事的公司不值得为之工作。
是的,但你会怎么做?返回?或抛出异常?它会像现在这样抛出异常。
我认为主要的问题是你的代码不是很优雅。内循环中的检查太多。冗余检查太多。
只是生硬,脱离我的头顶:
public int findArray(int[] largeArray, int[] subArray) {
int subArrayLength = subArray.length;
if (subArrayLength == 0) {
return -1;
}
int limit = largeArray.length - subArrayLength;
int i=0;
for (int i = 0; i <= limit; i++) {
boolean subArrayFound = true;
for (int j = 0; j < subArrayLength; j++) {
if (subArray[j] != largeArray[i+j]) {
subArrayFound = false;
break;
}
/* Sub array found - return its index */
if (subArrayFound) {
return i;
}
}
/* Return default value */
return -1;
}
你可以继续检查第一个元素,这样就不会有为数组中每个元素设置boolean和for循环的开销。然后你会看着
public int findArray(int[] largeArray, int[] subArray) {
int subArrayLength = subArray.length;
if (subArrayLength == 0) {
return -1;
}
int limit = largeArray.length - subArrayLength;
int i=0;
for (int i = 0; i <= limit; i++) {
if (subArray[0] == largeArray[i]) {
boolean subArrayFound = true;
for (int j = 1; j < subArrayLength; j++) {
if (subArray[j] != largeArray[i+j]) {
subArrayFound = false;
break;
}
/* Sub array found - return its index */
if (subArrayFound) {
return i;
}
}
}
/* Return default value */
return -1;
}
答案 2 :(得分:4)
要在更大的整数数组中查找整数数组,可以使用与在较大字符串中查找子字符串相同的算法。为此,已知许多算法(参见Wikipedia)。特别是Boyer-Moore字符串搜索对于大型阵列是有效的。您尝试实现的算法效率不高(维基百科称之为'天真'实现)。
对于你的问题:
答案 3 :(得分:4)
Clean and improved code
public static int findArrayIndex(int[] subArray, int[] parentArray) {
if(subArray.length==0){
return -1;
}
int sL = subArray.length;
int l = parentArray.length - subArray.length;
int k = 0;
for (int i = 0; i < l; i++) {
if (parentArray[i] == subArray[k]) {
for (int j = 0; j < subArray.length; j++) {
if (parentArray[i + j] == subArray[j]) {
sL--;
if (sL == 0) {
return i;
}
}
}
}
}
return -1;
}
答案 4 :(得分:3)
以下是使用KMP模式匹配算法的方法。该解决方案需要O(n + m)。其中n =大数组的长度,m =子数组的长度。有关更多信息,请查看https://en.wikipedia.org/wiki/KMP_algorithm 蛮力需要O(n m)。我刚检查了Collections.indexOfSubList方法也是O(n m)。
public static int subStringIndex(int[] largeArray, int[] subArray) {
if (largeArray.length == 0 || subArray.length == 0){
throw new IllegalArgumentException();
}
if (subArray.length > largeArray.length){
throw new IllegalArgumentException();
}
int[] prefixArr = getPrefixArr(subArray);
int indexToReturn = -1;
for (int m = 0, s = 0; m < largeArray.length; m++) {
if (subArray[s] == largeArray[m]) {
s++;
} else {
if (s != 0) {
s = prefixArr[s - 1];
m--;
}
}
if (s == subArray.length) {
indexToReturn = m - subArray.length + 1;
break;
}
}
return indexToReturn;
}
private static int[] getPrefixArr(int[] subArray) {
int[] prefixArr = new int[subArray.length];
prefixArr[0] = 0;
for (int i = 1, j = 0; i < prefixArr.length; i++) {
while (subArray[i] != subArray[j]) {
if (j == 0) {
break;
}
j = prefixArr[j - 1];
}
if (subArray[i] == subArray[j]) {
prefixArr[i] = j + 1;
j++;
} else {
prefixArr[i] = j;
}
}
return prefixArr;
}
答案 5 :(得分:1)
之前发布的一些优化代码:
public int findArray(byte[] largeArray, byte[] subArray) {
if (subArray.length == 0) {
return -1;
}
int limit = largeArray.length - subArray.length;
next:
for (int i = 0; i <= limit; i++) {
for (int j = 0; j < subArray.length; j++) {
if (subArray[j] != largeArray[i+j]) {
continue next;
}
}
/* Sub array found - return its index */
return i;
}
/* Return default value */
return -1;
}
答案 6 :(得分:0)
我建议进行以下改进:
i <= largeArray.length-subArray.length
,以避免在循环内进行测试largeArray[i] == subArray[0]
)答案 7 :(得分:0)
int findSubArr(int[] arr,int[] subarr)
{
int lim=arr.length-subarr.length;
for(int i=0;i<=lim;i++)
{
int[] tmpArr=Arrays.copyOfRange(arr,i,i+subarr.length);
if(Arrays.equals(tmpArr,subarr))
return i; //returns starting index of sub array
}
return -1;//return -1 on finding no sub-array
}
<强>更新强>
通过重用相同的int数组实例:
int findSubArr(int[] arr,int[] subarr)
{
int lim=arr.length-subarr.length;
int[] tmpArr=new int[subarr.length];
for(int i=0;i<=lim;i++)
{
System.arraycopy(arr,i,tmpArr,0,subarr.length);
if(Arrays.equals(tmpArr,subarr))
return i; //returns starting index of sub array
}
return -1;//return -1 on finding no sub-array
}
答案 8 :(得分:0)
来自String的#indexOf:
/**
* Code shared by String and StringBuffer to do searches. The
* source is the character array being searched, and the target
* is the string being searched for.
*
* @param source the characters being searched.
* @param sourceOffset offset of the source string.
* @param sourceCount count of the source string.
* @param target the characters being searched for.
* @param targetOffset offset of the target string.
* @param targetCount count of the target string.
* @param fromIndex the index to begin searching from.
*/
static int indexOf(char[] source, int sourceOffset, int sourceCount,
char[] target, int targetOffset, int targetCount,
int fromIndex) {
if (fromIndex >= sourceCount) {
return (targetCount == 0 ? sourceCount : -1);
}
if (fromIndex < 0) {
fromIndex = 0;
}
if (targetCount == 0) {
return fromIndex;
}
char first = target[targetOffset];
int max = sourceOffset + (sourceCount - targetCount);
for (int i = sourceOffset + fromIndex; i <= max; i++) {
/* Look for first character. */
if (source[i] != first) {
while (++i <= max && source[i] != first);
}
/* Found first character, now look at the rest of v2 */
if (i <= max) {
int j = i + 1;
int end = j + targetCount - 1;
for (int k = targetOffset + 1; j < end && source[j]
== target[k]; j++, k++);
if (j == end) {
/* Found whole string. */
return i - sourceOffset;
}
}
}
return -1;
}
答案 9 :(得分:0)
首先是您可能的原因:
final
构造函数的类private
。null
字段隐式检查length
,该字段将引发NullPointerException
。只有在largeArray.length == 0
和subArray == null
的情况下才能通过。更多潜在原因:
NullPointerException
)以及如果找不到第二个数组或者它是空的则返回值。boolean
和break
),进一步降低了易读性。largeArray.length <= i+j
不容易掌握。应该在循环之前检查,改善整个过程中的性能。subArray[j] != largeArray[i+j]
的操作数。对我来说似乎更自然。null
数组,第一个数组为空,两个数组都为空,第一个数组包含在第二个数组中,第二个数组包含多次等。)。testFindArrayExistsVeryComplex
?缺少的练习是数组参数的组件类型的规范,分别是方法的签名。组件类型是基本类型还是引用类型会产生巨大差异。 The solution of adietrich假设一个引用类型(因此可以作为进一步的改进),我假设一个原始类型(int
)。
所以这是我的镜头,专注于代码/忽视文档和测试:
public final class ArrayUtils {
// main method
public static int indexOf(int[] haystack, int[] needle) {
return indexOf(haystack, needle, 0);
}
// helper methods
private static int indexOf(int[] haystack, int[] needle, int fromIndex) {
for (int i = fromIndex; i < haystack.length - needle.length; i++) {
if (containsAt(haystack, needle, i)) {
return i;
}
}
return -1;
}
private static boolean containsAt(int[] haystack, int[] needle, int offset) {
for (int i = 0; i < needle.length; i++) {
if (haystack[i + offset] != needle[i]) {
return false;
}
}
return true;
}
// prevent initialization
private ArrayUtils() {}
}
答案 10 :(得分:0)
byte[] arr1 = {1, 2, 3, 4, 5, 6, 7, 7, 8, 9, 1, 3, 4, 56, 6, 7};
byte[] arr2 = {9, 1, 3};
boolean i = IsContainsSubArray(arr1, arr2);
public static boolean IsContainsSubArray(byte[] Large_Array, byte[] Sub_Array){
try {
int Large_Array_size, Sub_Array_size, k = 0;
Large_Array_size = Large_Array.length;
Sub_Array_size = Sub_Array.length;
if (Sub_Array_size > Large_Array_size) {
return false;
}
for (int i = 0; i < Large_Array_size; i++) {
if (Large_Array[i] == Sub_Array[k]) {
k++;
} else {
k = 0;
}
if (k == Sub_Array_size) {
return true;
}
}
} catch (Exception e) {
}
return false;
}
答案 11 :(得分:0)
来自番石榴的代码:
import javax.annotation.Nullable;
/**
* Ensures that an object reference passed as a parameter to the calling method is not null.
*
* @param reference an object reference
* @param errorMessage the exception message to use if the check fails; will be converted to a
* string using {@link String#valueOf(Object)}
* @return the non-null reference that was validated
* @throws NullPointerException if {@code reference} is null
*/
public static <T> T checkNotNull(T reference, @Nullable Object errorMessage) {
if (reference == null) {
throw new NullPointerException(String.valueOf(errorMessage));
}
return reference;
}
/**
* Returns the start position of the first occurrence of the specified {@code
* target} within {@code array}, or {@code -1} if there is no such occurrence.
*
* <p>More formally, returns the lowest index {@code i} such that {@code
* java.util.Arrays.copyOfRange(array, i, i + target.length)} contains exactly
* the same elements as {@code target}.
*
* @param array the array to search for the sequence {@code target}
* @param target the array to search for as a sub-sequence of {@code array}
*/
public static int indexOf(int[] array, int[] target) {
checkNotNull(array, "array");
checkNotNull(target, "target");
if (target.length == 0) {
return 0;
}
outer:
for (int i = 0; i < array.length - target.length + 1; i++) {
for (int j = 0; j < target.length; j++) {
if (array[i + j] != target[j]) {
continue outer;
}
}
return i;
}
return -1;
}
答案 12 :(得分:0)
我会以三种方式做到这一点:
不使用任何导入,即使用纯Java语句。
在某种程度上或某些程度上使用JAVA核心API。
1,2和3都在上面的答案中显示。这是我身边的方法2:
public static void findArray(int[] array, int[] subArray) {
if (subArray.length > array.length) {
return;
}
if (array == null || subArray == null) {
return;
}
if (array.length == 0 || subArray.length == 0) {
return;
}
//Solution 1
List<Integer> master = Arrays.stream(array).boxed().collect(Collectors.toList());
List<Integer> pattern = IntStream.of(subArray).boxed().collect(Collectors.toList());
System.out.println(Collections.indexOfSubList(master, pattern));
//Solution2
for (int i = 0; i <= array.length - subArray.length; i++) {
String s = Arrays.toString(Arrays.copyOfRange(array, i, i + subArray.length));
if (s.equals(Arrays.toString(subArray))) {
System.out.println("Found at:" + i);
return;
}
}
System.out.println("Not found.");
}
答案 13 :(得分:0)
使用Java 8和lambda表达式:
String[] smallArray = {"1","2","3"};
final String[] bigArray = {"0","1","2","3","4"};
boolean result = Arrays.stream(smallArray).allMatch(s -> Arrays.stream(bigArray).anyMatch(b -> b.equals(s)));
PS:具有 final String[] bigArray
来封闭lambda表达空间很重要。
答案 14 :(得分:0)
仅供参考:如果目标只是搜索数组 y 是否是数组 x 的子集,我们可以使用:
val x = Array(1,2,3,4,5)
val y = Array(3,4,5)
val z = Array(3,4,8)
x.containsSlice(y) // true
x.containsSlice(z) // false