java 数组到底是如何工作的

发布于 2024-11-15 14:27:48 字数 1234 浏览 3 评论 0原文

有人能解释一下数组在 Java 中是如何工作的吗?

我对下面的代码感到惊讶:

        Object test = new Object[2][2];
        Object test2 = new Object[] {
                new Object[2],new Object[2]
        };
        Object test3 = new Object[2][];
        ((Object[])test3)[0] = new Object[2];
        ((Object[])test3)[1] = new Object[2];
        System.out.println(test instanceof Object[]);
        System.out.println(test instanceof Object[][]);
        System.out.println(test2 instanceof Object[]);
        System.out.println(test2 instanceof Object[][]);
        System.out.println(test3 instanceof Object[]);
        System.out.println(test3 instanceof Object[][]);

只有test2不是Object[][]的实例,

运行时有什么区别?

编辑:我看到一些答案。 Jon Skeet,请注意我可以这样做:

Object[] test4 = (Object [])test;
test4[0] = "blaaa";
test4[1] = "toto";
System.out.println(test4);

test instanceof Object[] 返回 true,并且在运行时不会引发任何异常。根据 Sierra & SCJP 的书,贝茨,测试 IS-A Object[][] 但也是一个 Object[]

但是当尝试使用“test4[0] =”blaaa“;”重新分配新值时,我得到一个异常: 线程“main”中的异常 java.lang.ArrayStoreException:java.lang.String at Main.main(Main.java:24)

所以看起来在运行时,test和test2都是IS-A Object[],并且都包含对象数组,但只有其中之一是IS-A Object[][]

Can someone explain me how arrays really work in Java.

I was surprised by the following code:

        Object test = new Object[2][2];
        Object test2 = new Object[] {
                new Object[2],new Object[2]
        };
        Object test3 = new Object[2][];
        ((Object[])test3)[0] = new Object[2];
        ((Object[])test3)[1] = new Object[2];
        System.out.println(test instanceof Object[]);
        System.out.println(test instanceof Object[][]);
        System.out.println(test2 instanceof Object[]);
        System.out.println(test2 instanceof Object[][]);
        System.out.println(test3 instanceof Object[]);
        System.out.println(test3 instanceof Object[][]);

only test2 is not an instance of Object[][]

What is the distinction at runtime?

Edit: i see some answers.
Jon Skeet, please notice that i can do:

Object[] test4 = (Object [])test;
test4[0] = "blaaa";
test4[1] = "toto";
System.out.println(test4);

test instanceof Object[] returns true, and no exception is raised at runtime on the cast. According to the SCJP book of Sierra & Bates, test IS-A Object[][] but also a Object[]

But when trying to reassigning a new value with "test4[0] = "blaaa";", i get an exception:
Exception in thread "main" java.lang.ArrayStoreException: java.lang.String
at Main.main(Main.java:24)

So it seems at runtime, both test and test2 IS-A Object[], and both contains object arrays, but only one of them IS-A Object[][]

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

萤火眠眠 2024-11-22 14:27:48

Test2 仅被声明为对象数组。它包含的对象恰好也是数组,但这没有声明。这就是区别。

Test2 is only declared to be an array of objects. The objects it contains happen to be arrays as well, but this is not declared. That is the distinction.

陌伤浅笑 2024-11-22 14:27:48

test2 指的是包含两个元素的数组。它的类型只是Object[] - 因此这些元素可以引用任何对象。特别是,您可以这样写:

// Valid
Object[] foo = (Object[]) test2;
foo[0] = "hello";

不会 适用于 test

// Invalid - test isn't just an Object[], it's an Object[][]
Object[] foo = (Object[]) test;
test[0] = "hello";

因为 test 引用的数组的元素类型是 < code>Object[] 而不是 Object。数组“知道”每个元素应该为 null 或对 Object[] 的引用,因此 VM 将阻止它存储字符串。

您可以将 test 转换为 Object[],就像将 String[] 转换为 Object[] 一样 - 这就是所谓的数组协方差,在我看来,允许它是一个错误。正如我们所见,虚拟机必须在执行时检查存储。

test2 refers to an array of two elements. Its type is just Object[] - so those elements can refer to any objects. In particular, you could write:

// Valid
Object[] foo = (Object[]) test2;
foo[0] = "hello";

whereas that wouldn't work for test:

// Invalid - test isn't just an Object[], it's an Object[][]
Object[] foo = (Object[]) test;
test[0] = "hello";

because the element type of the array that test refers to is Object[] rather than Object. The array "knows" that each element should be null or a reference to an Object[], so the VM will prevent it from storing a string.

You can convert test to an Object[] in the same way that you can convert a String[] to an Object[] - that's called array covariance, and it was a mistake to allow it in my opinion. The VM has to check stores at execution time, as we've seen.

甜`诱少女 2024-11-22 14:27:48

我没有找到任何完整的答案来回答我的问题,我会这样做......

读完 SCJP 书后,我对它更加清楚了。它只是在泛型一章而不是数组一章中进行了处理。 (泛型与数组)
乔恩·斯基特的回答很好,但对我来说似乎不完整。

所以你必须理解泛型和数组的区别。

泛型只是一种“编译安全性”。运行时没有检查。这意味着,通过以下技巧,您可以将字符串对象插入到集合中

public static void main(String [] args) {
    Set<Integer> set = new HashSet<Integer>();
    set.add(1);
    set.add(2);
    addString(set,"test");
    for ( Object o : (Set)set ) {
        System.out.println(o);
    }
    for ( Object o : set ) {
        System.out.println(o);
    }
}

public static void addString(Set set,String s) {
    set.add(s);
}

输出为:

1
2
test
1
2
ClassCastException

http://www.ideone .com/nOSQz

请注意,代码编译(带有警告)并运行得很好,直到您将该集合与 Set 引用一起使用,因为有一个隐式强制转换会尝试将字符串转换为整数...

这样做的原因有很多(并非全部在书中公开),例如追溯兼容性,以及需要能够调用遗留代码(非泛型),同时提供非泛型集合。

如果您不调用任何遗留代码而只调用泛型代码,那么您不应该遇到任何这样的问题,您的集合只包含您想要的内容,因为编译器处理了它,并且不需要在运行时进行检查...

因为泛型在运行时没有检查,并尝试防止在集合中插入错误的项目,禁止将 List 转换为 List,或致电method(List) 带有 List 参数,因为这意味着您可以在 List 中插入 Cat > 通过操作 List...


数组的工作方式不同。

正如 Jon Skeet 所说,数组具有类型协方差。

因此我们可以将 Dog[] 转换为 Animal[],因为 Dog 是一种动物。

与泛型一样,Java 希望尽量避免将 Cat 插入到 Dog 数组中。
但由于协变性,它无法在编译时完成。
就像 Jon Skeet 一样,我同意这种协方差也许不是一个好主意,但它是 Java 的遗产......
因此,与泛型相反,数组确实具有运行时检查,以防止将猫插入狗数组。

在运行时,JVM 知道应该在数组中插入什么,而对于泛型则不知道。


所以,回到我最初的问题

Object test = new Object[2][2];
Object[] test2 = (Object [])test;
test2[0] = "blaaa";
test2[1] = "toto";
System.out.println(test2);

test(2D 数组)可以转换为 test2(1D 数组),但在幕后它仍然是一个 2D 数组,最终是一个 1D 数组 A1,预计将被其他 1D 数组填充A2 包含对象。

这就是为什么当我在 A1(最后是 test2)中尝试插入一个 String 时引发 ArrayStoreException,它最终是一个 Object,而不是一个 Object[]


总结:

由于使用 1D 和 2D 数组,可能会出现一些混乱转换为一维数组,但对于此代码来说,它是完全相同的:

数组:

Dog[] dogs = new Dog[1];
dogs[0] = new Dog();
Animal[] animals = (Animal [])dogs;
animals[1] = new Cat();

这在运行时在第四行失败。
而且你无法将猫插入到狗数组中。

如果我们对泛型做同样的事情,

Set<Dog> dogs = new HashSet<Dog>();
dogs.add( new Dog() );
Set<Animal> animals = (Set<Animal>) dogs;
animals.add( new Cat() );

由于第三行,这不会编译。
但是通过使用旧的泛型代码,您可以将猫插入到狗集中。

I don't find any complete answer to my question to i'll do it...

After finishing the reading of SCJP book, it's a lot clearer to me. It was just treated on the generics chapter rather than arrays one. (Generics vs Arrays)
Jon Skeet answer is good but seems incomplete to me.

So you have to understand the differences with generics and arrays.

Generics are just a "compilation security". There is no check at runtime. This means, by the following trick, you CAN insert String objets into a Set

public static void main(String [] args) {
    Set<Integer> set = new HashSet<Integer>();
    set.add(1);
    set.add(2);
    addString(set,"test");
    for ( Object o : (Set)set ) {
        System.out.println(o);
    }
    for ( Object o : set ) {
        System.out.println(o);
    }
}

public static void addString(Set set,String s) {
    set.add(s);
}

The output is:

1
2
test
1
2
ClassCastException

http://www.ideone.com/nOSQz

Note that the code compile (with a warning) and runs perfectly fine, until you use that set with a Set<Integer> reference because there is an implicit cast that tries to cast String to Integer...

This has been done this way because of many reasons (not all exposed on the book) like retrocompatibility, and the need to be able to call legacy code (non generic), while providing non generic collections.

If you don't call any legacy code but only generics code, you should not have any problem like that, your collection contains only what you want since the compiler handled it, and there is no need to do checks at runtime...

Since generics do not have checks at runtime and try to prevent inserting the wrong item in a collection, it is forbidden to cast a List<Dog> to a List<Animal>, or to call a method(List<Animal>) with a List<Dog> param, because this would mean you could insert a Cat in a List<Dog> by manipulating a List<Animal>...


Arrays do not work the same.

An array have, like Jon Skeet sais, type covariance.

So we can convert a Dog[] to an Animal[] since Dog is an animal.

Like for generics, Java want to try to avoid inserting Cats to a Dog array.
But because of covariance, it can't be done at compile time.
Like Jon Skeet, i agree this covariance is perhaps not a good idea, but it's java legacy...
So, in opposition to generics, arrays do have runtime check to prevent inserting cats to a dog array.

At runtime, the JVM knows what should be inserted in my arrays, while with generics it doesn't.


So, back to my initial question

Object test = new Object[2][2];
Object[] test2 = (Object [])test;
test2[0] = "blaaa";
test2[1] = "toto";
System.out.println(test2);

test (2D array) can be casted to test2 (1D array), but behind the scene it is still a 2D array, which is finally, a 1D array A1 that expects to be filled with other 1D arrays A2's which contains Objects.

That's why an ArrayStoreException is raised when in A1 (finally test2) i try to insert a String, which is finally an Object but not an Object[]


To conclude:

There may be a little confusion because of using 1D and 2D arrays that can be casted to 1D arrays, but it would be exactly the same for this code:

Arrays:

Dog[] dogs = new Dog[1];
dogs[0] = new Dog();
Animal[] animals = (Animal [])dogs;
animals[1] = new Cat();

This fails at runtime on the 4th line.
And there is no way you can insert a cat into a dog array.

And if we do the same with generics

Set<Dog> dogs = new HashSet<Dog>();
dogs.add( new Dog() );
Set<Animal> animals = (Set<Animal>) dogs;
animals.add( new Cat() );

This doesn't compile because of the 3rd line.
But by using legacy generics code you CAN insert a cat to a dogs set.

谁与争疯 2024-11-22 14:27:48

test2引用的对象是一个Object[]。

Instanceof 正在测试 test2 引用的对象的类型,而不是数组内容的类型。

运行时数组的内容是Object[],它可以装入Object[],因为Object[]是对象。

The object referenced by test2 is an Object[].

Instanceof is testing the type of the object referenced by test2, not the type of the array's contents.

The array's contents at runtime are Object[]s, which can fit into the Object[] because Object[]s are Objects.

久隐师 2024-11-22 14:27:48

test2 是您可以放入任何Object 的东西,因为它是通过new Object[] 创建的。您只能将 Object[] 放入 testtest3 中,因为它们是通过更严格的构造函数创建的:new Object[ ][]

test2 is something that you can put any Object into because it was created via new Object[]. You can only put Object[]s into test and test3 because they were created via a constructor that is stricter : new Object[][].

笑看君怀她人 2024-11-22 14:27:48

您已将 test2 定义为

Object test2 = new Object[];     // This is a plain array of Objects.

You have defined test2 as

Object test2 = new Object[];     // This is a plain array of Objects.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文