java 6 中的数组复制

Java语言包含了一些精彩的“古董”。例如仍然不支持在switch语句中使用String对象。(不过已经更新为可以支持enum类型了) 。这个理由似乎可以追溯到switch语句是源自于c语言。不论如何,在switch语句中不能使用string对象进行判断对我来说都是很不方便的。
System.arraycopy, 快速拷贝数组内容到另一数组的方法,似乎也有类似的传统。方法名称看上去像一个旧风格的C函数memcpy,这意味着Java的数组复制方法从第一个版本以来就没有改变.
arraycopy这个方法是一个系统级别的静态方法,具备很强大的功能,通常的写法如下:
System.arraycopy(sourceArray, sourceStartIndex, targetArray, targetStartIndex, length);
该arraycopy方法不仅仅是支持复制到目标数组的全部内容,此外,arraycopy允许设置源和目标的起始索引以及长度来进行内容元素的拷贝。这种额外的灵活性是很有用的,尽管它可能不是我在通常情况下所需要的。例如ArrayList类在插入一个元素时就是用arraycopy来转移数组的内容,这个方法执行得非常快: 在底层不是Java实现的,而是JVM调用的本地方法来实现。
大多情况下,我需要复制整个数组而不是子集。 我也不需要频繁的转变数组中的元素。 我的System.arrayCopy调用大多需要两行代码:
String[] source = { "alpha", "beta", "gamma" };
String[] target = new String[source.length];
System.arraycopy(source, 0, target, 0, source.length);
更进一步,出于重用性的需要,需要多一点儿的Java代码,如下:
private final <T> T[] copy(T[] source) {
T[] target = new T[source.length]; // This will not compile!
System.arraycopy(source, 0, target, 0, source.length);
return target;
}
好的,这段代码并不能真正的工作,编译会出错:
Cannot create a generic array of T
通过反射可以解决这个编译问题,下面是可以正确编译的代码:
private final <T> T[] copy(T[] source) {
Class type = source.getClass().getComponentType();
T[] target = (T[])Array.newInstance(type, source.length);
System.arraycopy(source, 0, target, 0, source.length);
return target;
}
Java编译器会显示一个警告“Type safety: the case from Object to T[] is actually checking against the erased type Object[]” 使用反射意味着没有足够的信息告诉编译器这是否是一个错误的cast,但这确实是一个快速的能够绕过那个讨厌的警告的方法。
@SuppressWarnings("unchecked")
private final <T> T[] copy(T[] source) {
Class type = source.getClass().getComponentType();
T[] target = (T[])Array.newInstance(type, source.length);
System.arraycopy(source, 0, target, 0, source.length);
return target;
}
包装了这个copy方法,我的客户端代码就很简单了,如下:
String[] source = { "alpha", "beta", "gamma" };
String[] target = copy(source);
好了,我已经快速建立了自己的通用拷贝方法。

[size=large]copyof的介绍[/size]
在Java 6中,我不再需要推出自己的实用方法:Sun公司推出了复制一个数组的直接支持:copyof, 基本使用方法如下:
targetArray = Arrays.copyOf(sourceArray, length);
我写了几个JUnit的测试,以证明copyOf法(及其变种)。 这里的第一个测试,显示其基本功能:
@Test
public void genericArrayCopyOf() {
Number[] source = { new Double(5.0), new Double(10.0) };
Number[] target = Arrays.copyOf(source, source.length);
assertEquals(source, target);
}
JUnit4的一个不错的新特点是它能够使用assertEquals比较两个数组。首先比较JUnit的每个数组的长度,如果两个数组的长度相同,jUnit再比较数组的每个元素中是否相等,如果数组不同,JUnit则提供了一个失败的消息,并显示了不一致的值。
Java重载copyOf以支持原始类型,以及扩展使用。下一项测试显示了这一点:
@Test
public void copyOfWithRange() {
String[] source = { "0", "1", "2", "3", "4" };
String[] target = Arrays.copyOfRange(source, 2, 4);
assertEquals(new String[] { "2", "3" }, target);
}
这是一个数组复制原始使用的测试
@Test
public void primitives() {
int[] source = { 0, 1, 2, 3, 4 };
int[] target = Arrays.copyOfRange(source, 4, 5);
assertEqualsPrim(new int[] { 4 }, target);
}
我写的方法assertEqualsPrim因为JUnit 4不比较两个数组中包含的原始对象。它仅比较两个数组对象的版本。 使用assertEquals比较两个数组意味着Java进行内存地址比较。不过没关系,assertEqualsPrim是很容易写的:
static void assertEqualsPrim(int[] expected, int[] actual) {
if (expected.length != actual.length)
fail(String.format("expected length = %s, actual length = %s",
expected.length, actual.length));
for (int i = 0; i < expected.length; i++) {
if (expected[i] != actual[i])
fail(String.format(
"mismatch at index %d: expected [%s] but was [%s]", i,
expected[i], actual[i]));
}
如果我想通过强制转换返回结果的方式来进行数组拷贝,就会出现下面的测试失败:
@Test
public void genericArrayCopyOfWithNewType() {
Number[] source = { new Double(5.0), new Double(10.0) };
Double[] target = (Double[])Arrays.copyOf(source, source.length);
assertEquals(source, target); // fail!
}
好在java 6中可以通过声明新类型的方式进行数组拷贝
@Test
public void genericArrayCopyOfWithNewType() {
Number[] source = { new Double(5.0), new Double(10.0) };
Double[] target = Arrays.copyOf(source, source.length, Double[].class);
assertEquals(source, target);
}

[size=large]性能以及替代方案[/size]

其实我可以使用Clone方法实现数组复制,所以技术上copyOf是没有必要的。不过,我匆匆写的一个性能测试表明,克隆是相当缓慢。他们还表明,copyOf比arraycopy还慢。所以我将使用新的copyOf方法。

广告时间:轻松阅读尽在[url=http://www.article-reading.com][b]阅读地带[/b][/url]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值