【问题标题】:Should Java treat arrays as objects?Java 应该将数组视为对象吗?
【发布时间】:2009-02-18 03:53:35
【问题描述】:

我经常认为允许将数组用作具有自己的方法的适当对象是一个好主意,而不是依赖像 Arrays、Arrays 和 ArrayUtils 这样的辅助类。

例如:

ints.sort();                 // Arrays.sort(ints);
int[] onemore = ints.add(8); // int[] onemore = ArrayUtils.add(ints, 8);

我确信我不是第一个有这个想法的人,但我很难找到以前写过这个想法的其他人。谁能帮我提供一些关于这个主题的参考资料?

这是一个好主意还是坏主意,为什么?

实现起来有多容易?

其他一些示例可能包括(但不要挂断它们,它们与问题本身无关):

int[] ints = {5,4,3,2,1};

// Arrays.sort (ints);
ints.sort();

// int pos = Arrays.asList(ints).indexOf (5);
// int pos = ArraysUtils.indexOf (ints, 5);
int pos = ints.indexOf (5);

// Arrays.reverse (ints);
ints.reverse();

Array<Integer> array = ints; // cast to super class.

// int length = Array.getLength (array);
int length = array.getLength();

// Object n = Array.get (array, 3);
Object n = array.get (3);

// Array.set (array, 3, 7);
array.set (3, 7);

Object obj = array;
// if (obj instanceof int[])
//     System.out.println(Array.toString((int[]) obj));
// else if (....)
System.out.println (obj);

【问题讨论】:

  • 不确定你是否把米奇带到那里,比尔 :-) 这是对某人问题的转贴,因为我会写它,希望能教育他们。这就是我 wiki 的原因 - 我没有代表嫖娼。
  • 我正在关注其他线程。我只是忍不住开玩笑。现在我仔细想想,这是一个不错的问题。
  • @Peter Lawrey,如果这个问题仍然存在(它可能不会,但我会忍受),让我知道你想要接受哪个答案,因为它是你最初的问题。否则我只会接受大约一周内得票最高的那个。
  • 也有不一致的地方。虽然数组不是对象,但它们有一个 .lenght 属性,这对于原始类型来说是反直觉的。

标签: java arrays


【解决方案1】:

数组不是 Java 中的类是有充分理由的——它们很好地映射到人们从使用 C 风格语言的经验中对数组应该如何工作的理解。使数组成为低级容器而不是对象也有性能原因。因此,有时使用原始数组而不是集合会带来性能优势。

如果你想使用对象,你应该只使用一个集合(一个 ArrayList 是一个集合的例子)。它可能很笨重,但集合提供了您似乎想要的良好方法访问类型。

【讨论】:

  • 我同意在 95 年将数组视为原始数组是有意义的,但我认为是时候采用更加面向对象的方法了。就像自动装箱/取消装箱一样,这会处理具有更紧密连接的数组已经可用的功能。
  • 数组不是原语。阅读 Class.isPrimitive 的文档。
  • Vector 是一个遗留类,不要使用。使用 ArrayList 代替
  • Vector 不是一个遗留类,它是当今 ArrayList 的线程安全版本。
  • @Esko Vector 一个遗留类。见stackoverflow.com/questions/1386275/…
【解决方案2】:

这些方法开始看起来很像 ruby​​ 或 python 习语。不幸的是,你不能在 java 中做到这一点(希望你能做到)。

首先,正如其他人所指出的那样,集合类为您完成了这项工作。另一方面, myarray.sort() 不是很好,因为您可以创建尚未定义排序的对象数组。假设我有

 Foo[] foos; 

而 Foo 是不可比的。 foos.sort() 上会发生什么?我们绝对不希望它只适用于原语

int[] ints; 
ints.sort(); //legal
Object[] objects;
objects.sort(); //illegal

您当然不能让编译器只允许可比较对象的语法。一旦你得到类似的东西

 myarray.add(new Foo());

这有点毫无意义,因为 java 中的数组是不可增长的。

如果打印出一个数组不会给你带来那么无用,那很好

([I'm an array(*&(*

不过,垃圾。

【讨论】:

  • ArrayUtils.add()、addAll() 和 remove() 返回一个新数组,其中添加了元素。
  • 当您不想使用 ArrayList 的开销时,这些方法对于管理数组非常有用。
【解决方案3】:

是的。但我认为Java 根本不应该有任何原语。我认为 Java 中的原语是从语言的简洁性中分离出来的。 Java 中的一切都应该是对象。 对象是分配在还是应该是JVM的实现细节不是一种语言结构。但我认为我的观点可能比大多数人更激进。

在自动装箱之前,处理图元和对象非常麻烦。

如果数组是对象并且可以自动装箱(和通用化!),我们可以有类似的东西

Array<Integer> myArray = new Integer[];
myArray.add(8);
int x = myArray[0];
....

Array<Class<? extends MyBaseObject>> myArray = {ExtendedObject.class}
ExtendedObject object = myArray[0].newInstance();
....

【讨论】:

  • 我同意不能被透明地视为对象的基元是不幸的。编译器/JVM 应该能够解决它。 ArrayUtils 有两个方法 toObject() 和 toPrimitive()。它可以有另一种方法来更改数组的类型。
  • 我建议 Array 和 Integer[] 是写同一件事的两种方式。 ;)
  • 在 Java 中修复此类问题为时已晚。看看它们从一开始就成为设计一部分的语言。例如,在 Scala 中,一切都是对象。
【解决方案4】:

是的,我认为数组应该有一个超出语言本身指定的 API 定义的 API。特别是,Foo[] 没有实现Iterable&lt;Foo&gt;,这很烦人。是的,我知道它很容易包装 - 但你必须这样做很烦人。

.NET 基本上做到了这一点,Array 类型主要用于非泛型访问,以及被视为由实际数组实现的各种泛型接口。例如:

IList<int> intList = new int[10];

(它有助于 .NET 泛型比 Java 更好地处理原始类型。)

我看到的这种方法的唯一缺点是数组在 .NET 中可以是协变的,但普通泛型不是,而且与非从零开始的数组和矩形数组有些混淆。

顺便说一句,很多人在这个线程中将数组称为原语。虽然它们当然有特殊处理,但它们并没有被定义为原语。来自language spec, section 4.2

原始类型由 Java 编程语言预定义并由其命名 保留关键字(第 3.9 节):

原始类型: 数字类型 布尔值 数字类型: 积分型 浮点型 IntegralType:其中之一 字节短整数长字符 FloatingPointType:其中之一 浮动双

【讨论】:

    【解决方案5】:

    在我回答应该之前,我会告诉你这个问题的状态。

    在 Java 中,数组被视为对象——例如,您可以将它们存储在 List 中。但是它们是特殊的对象,因为它们继承自 Object 但没有实例化或使用相同的语法访问(感谢 Peter 的更正)。它们被 JVM 积极优化以解决明显的性能问题,因此数组的存储方式取决于实现。

    因此,Sun 的论点是,如果他们为阵列提供对象 API,则可能需要具备某些硬件、软件或其他规范特性。

    数组的唯一真正接口是 System.arrayCopy 静态方法或 Array.* 静态方法,它们将最有效地复制到数组/从数组中复制。

    响应应该;它已经解决了,虽然标准会比答案更好:使用 ArrayList。

    【讨论】:

    • 数组只实现了clone()方法,其他方法都继承自Object。并不是说它们没有实现/可用。他们是。全部。
    【解决方案6】:

    这不是一个糟糕的想法。事情是它已经在 Collections 上完成了。如果您想发疯,请尝试扩展 ArrayList。

    【讨论】:

    • ArrayList 缺乏的是对原语的有效支持,[] 运算符在您只需要一个对象时创建两个对象(ArrayList 和 Object[])。
    【解决方案7】:

    我相信如果不破坏许多级别的兼容性,这很难实现。

    • JVM 处理数组的方式与处理其他对象的方式略有不同。创建/初始化是不同的:没有为一件事调用构造函数。您想如何添加新方法?如果通过添加超类,你会调用它的构造函数吗?
    • 数组知道运行时元素的类型,泛型不知道。如果您想为所有数组添加新的超类(就像您在原始问题中建议的那样),您会使其通用吗?还要记住,数组在 Java 中是协变的,而泛型不是。
    • 数组具有 Java 语言规范中指定的固定方法/字段集(来自 Objects 的所有方法,在 JLS 中明确命名,长度字段)。根据这一点,添加新成员可能会破坏现有客户。 (即数组不是你的随机类)
    • 数组序列化可能也会受到影响
    • 我相信还有更多的实现细节会让这变得非常困难:-(
    • 为使用新方法而编译的程序无法在旧版 JVM 上运行。更糟糕的是,根据实现的不同,为旧 JVM 编译的程序可能无法在新 JVM 上使用修改后的数组。

    我想直接在“数组类”中有方法,我认为现在不可能实现。

    【讨论】:

      【解决方案8】:

      我个人喜欢一切都是对象的想法,例如,这已经在 smalltalk 中完成了。然而,把所有东西,甚至方法/函数都变成对象的后果是非常深远的(看看 smalltalk 就明白我的意思了)。在“一切都是对象”规则的应用中保持一致会导致语言看起来不再像 C(或 Java)。

      Java 非常有意识地设计为易于 C 程序员使用,并且在这方面取得了成功,但与 smalltalk 相比,它实际上并不是非常面向对象的。它作为 C 变体的遗产随处可见,例如在数组中以及存在原语的事实中。

      所以要回答“应该”的问题,我会说不,因为随着这种变化,Java 将不再是 Java。正如其他人所指出的那样,还有其他类可以让您处理可能已经使用 Java 中的对象在 C 中使用数组完成的任务,但是要完全摆脱原始数据类型和数组,最好留给另一种语言来完成。 ,恕我直言。

      【讨论】:

        【解决方案9】:

        对于那些错过了之前已关闭的帖子的人。其他一些例子包括

        int[] ints = {5,4,3,2,1};
        ints.sort(); // instead of Arrays.sort(ints);
        int pos = ints.indexOf(5); // instead of Arrays.asList(ints).indexOf(5); or ArraysUtils.indexOf(ints, 5);
        ints.reverse(); // instead of Arrays.reverse(ints);
        Array<Integer> array = ints; // cast to super class.
        int length = array.getLength(); // instead of Array.getLength(array);
        Object n = array.get(3); // instead of Array.get(array, 3);
        array.set(3, 7); // instead of Array.set(array, 3, 7);
        Object obj = array;
        System.out.println(obj); // prints [5,4,7,2,1] instead of having to
        // if (obj instanceof int[]) System.out.println(Array.toString((int[]) obj)); else if (....)
        
        int[] ints2 = ints.copyOf(2);
        int[] ints3 = ints.subArray(2,4);
        ints.sort(myComparator);
        List<Integer> list = ints.asList();
        Set<Integer> set = ints.asSet();
        long total = ints.sum();
        double avg = int.average();
        int max = ints.max();
        int max2 = ints.max(myComparator);
        http://commons.apache.org/lang/api/org/apache/commons/lang/ArrayUtils.html
        int[] onemore = ints.add(8); // instead of ArrayUtils.add(ints, 8);
        int[] moreInts = ints.addAll(ints2); // instead of ArraysUtils.addAll(ints, ints2);
        int[] oneless = int.remove(3); // instead of ArrayUtils.remove(ints, 3);
        Integer[] integers = int.toObject();
        int[] intsAgain = integers.toPrimitive();
        

        【讨论】:

        • 彼得,我会将其复制到问题中作为附录。
        【解决方案10】:

        我相信它不会那么难的一个原因是您可以通过修改 Object 间接添加方法(我同意一个真正的 hack,但它表明它有效) 通过给Object添加方法,如下代码

        int[] ints = {5, 4, 3, 2, 1};
        System.out.println("ints= "+ints);
        ints.sort();
        System.out.println("after sort() ints= "+ints);
        ints = ints.add(6);
        System.out.println("after add(6) ints= "+ints);
        

        打印

        ints= [5, 4, 3, 2, 1]
        after sort() ints= [1, 2, 3, 4, 5]
        after add(6) ints= [1, 2, 3, 4, 5, 6]
        

        这适用于 Java 5 和 6,编译器和 IDE 都按我的预期处理。

        【讨论】:

          猜你喜欢
          • 2015-02-12
          • 1970-01-01
          • 2019-07-20
          • 2010-12-13
          • 1970-01-01
          • 2021-08-26
          • 1970-01-01
          • 1970-01-01
          • 2011-12-15
          相关资源
          最近更新 更多