【问题标题】:Insert Method as part of an Array [closed]插入方法作为数组的一部分[关闭]
【发布时间】:2015-02-18 17:17:54
【问题描述】:

这种方法已经困扰了我好几个小时。它是 X 类型的 array 的一部分,应该采用任何数据类型。这是我第一次使用泛型类型array 和调试器。

我从测试中得到的错误是:

expected: 'hello' but was 'null' and 
0: ...ArrayOutOfBoundsException: 0

看起来ArrayOutOfBoundsException 处理也没有正确抛出。

我不知道该怎么办。我的删除功能似乎工作正常。

public void insert( int index, X item )
{
    if ( index > 0 || index < length )
    {
        length++;
        X[] obj2 = (X[]) new Object[obj.length - 1];
        for ( int i = 0; i == index; i++ )
        {
            obj2[i] = obj[i];
        }
        obj2[index] = item;
        for ( int i = index; i == length; i++ )
        {
            obj2[i + 1] = obj[i];
        }
        obj = obj2;
    }

    else
    {
        throw new java.lang.ArrayIndexOutOfBoundsException("" + index);
    }
}

@Test
public void testInsert()
{
    XArray<String> b = new XArray<String>();
    b.add("hello");
    b.add("bye");
    b.insert(1, "adios");
    assertEquals("hello", b.get(0));
    assertEquals("adios", b.get(1));
    assertEquals("bye", b.get(2));
    assertEquals(3, b.size());

    b.insert(1, "buenos dias");
    assertEquals(4, b.size());
    assertEquals("hello", b.get(0));
    assertEquals("buenos dias", b.get(1));
    assertEquals("adios", b.get(2));

    b.insert(0, "hi");
    assertEquals(5, b.size());
    assertEquals("hi", b.get(0));
    assertEquals("hello", b.get(1));

    b.insert(4, "see ya");
    assertEquals(6, b.size());
    assertEquals("see ya", b.get(4));
    assertEquals("bye", b.get(5));
}

【问题讨论】:

  • 做什么:第一件事:正确缩进你的代码。
  • 当您的index 等于0 时,您正在抛出java.lang.ArrayIndexOutOfBoundsException,您没有处理它!
  • @dsorenson 请提问。

标签: java arrays generics


【解决方案1】:

你有几个问题。

1) 假设length是一个实例变量,它反映了obj数组的length,那么你实际上要做如下测试:

if (index >= 0 && index <= length)

这将测试您的 new 项目索引是否为 0 或更大,或者是否比当前 obj 数组中的最大索引大 1(即等于当前数组长度) . 这确实稍微取决于您的预期合同我认为期望您可以在列表末尾“插入”是合理的,但您可能必须将其限制为仅在中间插入,因此仅当有另一个元素时。在这种情况下,你想要

if (length > 0 && index >= 0 && index < length)

注意:您测试的是&amp;&amp;,而不是||,因为您需要所有条件都为真。如果您想在开始时进行更标准的参数有效性检查,您可以这样做:

    if (index < 0 || index > length)
      throw new java.lang.ArrayIndexOutOfBoundsException("" + index);

这样做的好处是不再需要 else 块并略微减少代码嵌套。

2) 然后你增加this.length,然后你增加new Object[obj.length - 1];,这是没有意义的。我怀疑你想这样做:

  length++;
  @SuppressWarnings("unchecked")
  X[] obj2 = (X[]) new Object[this.length];

或者更好:

  @SuppressWarnings("unchecked")
  X[] obj2 = (X[]) new Object[++length];

3) 你的第一个循环是for ( int i = 0; i == index; i++ ),应该是:

for (int i = 0; i < index; i++)

这是因为循环条件语句不测试突破条件(即在此条件为真时停止),而是测试继续条件(即在 i 小于 index 时继续)

4) 你的第二个循环执行for ( int i = index; i == length; i++ ),应该这样做:

for (int i = index; i < length - 1; i++)

这是因为您的长度增加了,但是您使用 i 来引用原始数组索引,而不是更大的数组索引。

总而言之,我认为它看起来像下面的代码。

这里最大的问题可能是您的测试方法。一个大型测试中有多个测试。这并不能帮助您在不中断调试器的情况下隔离单个错误并找出您的方法出了什么问题(双关语不是故意的,但我只是看到了它,所以我会留下它)。理想情况下,如果您的单元测试是小型、集中和孤立的,则您不必使用调试器。此类测试的示例包含在末尾。

class XArray<X> {
  private int length;
  private X[] obj;
  public XArray() {
    this.obj = initBackingArray(0);
    this.length = 0;
  }
  private X[] initBackingArray(int length) {
    @SuppressWarnings("unchecked")
    X[] result = (X[])new Object[length];
    return result;
  }
  public X get(int index) {
    return this.obj[index];
  }
  public int size() {
    return this.length;
  }
  public void add(X item) {
    insert(length, item);
  }
  public void insert( int index, X item )
  {
    if (index >= 0 && index <= length)
    {
      @SuppressWarnings("unchecked")
      X[] obj2 = (X[]) new Object[++length];
      for (int i = 0; i < index; i++)
      {
        obj2[i] = obj[i];
      }
      obj2[index] = item;
      for (int i = index; i < length - 1; i++)
      {
        obj2[i+1] = obj[i];
      }
      obj = obj2;
    }

    else
    {
      throw new java.lang.ArrayIndexOutOfBoundsException("" + index);
    }
  }
}

示例测试:

@Test
public void testAddOnceWorks()   {
    XArray<String> b = new XArray<String>();
    b.add("hello");
}
@Test
public void testAddTwiceWorks()   {
    XArray<String> b = new XArray<String>();
    b.add("hello");
    b.add("bye");
}
@Test
public void testInsertOnEmptyWorks()   {
    XArray<String> b = new XArray<String>();
    b.insert(0, "hello");
}
@Test
public void testAddOnceInsertBeforeWorks()   {
    XArray<String> b = new XArray<String>();
    b.add("hello");
    b.insert(0, "hello");
}

【讨论】:

  • 我喜欢这些测试,因为你可以将你的代码减少到public void add(X item) { return; }public void insert(int index, X item) { return; } :D。
  • @Tom。实际上,尽管“添加有效输入并且不抛出”是根据 OP 的大型测试(实际上是true TDD)对合约的必要测试。 OP 还应该测试“添加一个无效输入并抛出”和“添加一个输入,并从该索引返回它”是无可争辩的,但我的意思不是写出 OP 需要测试类合同的每个测试,因为它没有指定(并且需要很长时间)。
  • @AndyBrown 谢谢你的帮助。我一直在尝试很多不同的东西,以至于当时我刚刚发布了我的代码。我什至做了我认为行不通的事情。答案还帮助我纠正了我以前没有发现的删除功能的类似问题。也谢谢你。
【解决方案2】:

首先,您的边界检查逻辑不正确。 index 的每个可能值都大于0 小于length。这会导致您尝试创建的 ArrayIndexOutOfBoundsException 不会被抛出。 (如果您越界访问数组,Java 可能会抛出自己的 AIOOBE。)

您应该确保满足这两个条件。使用&amp;&amp; 而不是||

if ( index > 0 && index < length )

另外,在将旧元素复制到新数组时,您需要意识到for 循环条件为true 意味着“继续”,而不是“停止”。在index 尚未到达length 时继续循环。将== 更改为&lt;

for ( int i = 0; i < index; i++ )

(另一个 for 循环需要进行类似的更改。)

【讨论】:

  • 你是对的,谢谢你的帮助!我有点在尝试在进入 for 循环之前将其抛出或嵌套 for 循环,但我的大脑已经变成糊状了。
猜你喜欢
  • 2020-04-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-24
  • 1970-01-01
  • 2017-04-06
  • 2011-05-05
相关资源
最近更新 更多