数组

》》数组为什么特殊

           @@   数组其他种类的容器之间的区别有三方面:效率 类型保存基本类型的能力

           @@  在 Java 中,数组是一种效率最高的存储和随机访问对象引用序列的方式。

           @@   数组就是一个简单的线性序列,这使得元素访问非常快速。 但是为这种速度所付出的

                   代价是数组对象的大小被固定,并且在其生命周期中不可改变。

            @@ 你可能会建议使用  ArrayList  ,它可以通过创建一个新实例,然后把旧实例中所有的引用

                    移到新实例中,从而实现更多空间的自动分配。

                    尽管通常应该首选  ArrayList   而不是数组,但是这种弹性需要开销,因此,ArrayList 的

                    效率比数组低很多

            @@  数组和容器都可以保证你不能滥用它们。无论你是使用数组还是容器,如果越界,都会得到一个

                    表示程序员错误的 RuntimeException 异常 。

            @@  数组可以持有基本类型,而泛型之前的容器则不能。

                     但是有了泛型,容器就可以指定并检查它们所持有对象的类型,并且有了自动包装机制,容器

                     看起来还能够持有基本类型。

             @@  数组之所以优于泛型之前的容器,就是因为你可以创建一个数组去持有某种基本类型。这意味着

                     你可以通过编译期检查来防止插入错误类型和抽取不当类型

             @@  数组使用 [ ] 来访问元素,而 List 使用的是 add( ) 和 get( ) 方法。

             @@  数组和 ArrayList  之间的相似性是有意设计的,这使得从概念上讲,这两者之间的切换是很

                     容易的。

             @@  随着自动包装机制的出现,容器已经可以与数组几乎一样方便地用于基本类型中了。数组硕果

                     仅存的优点就是效率。 

》》数组是第一级对象

              @@  无论使用哪种类型的数组,数组标识符其实只是一个引用,指向在堆中创建的一个真实对象,

                     这个(数组)对象用以保存指向其他对象的引用。

              @@  可以作为数组初始化语法的一部分隐式地创建数组对象,或者用 new 表达式显式地创建

              @@ 只读成员 length 是数组对象的一部分(事实上,这是唯一一个可以访问的字段或方法),表示

                     此数组对象可以存储多少元素

               @@ “ [ ] ” 语法是访问数组对象唯一的方式。

              @@  对象数组基本类型数组在使用上几乎是相同的;唯一的区别就是对象数组保存的是引用

                     基本类型数组直接保存基本类型的值

              @@ length 是数组的大小,而不是实际保存的元素个数。新生成一个对象数组对象时,其中所有的

                     引用都被自动初始化为  null  , 所以检查其中的引用是否为 null , 即可知道数组的某个对象是否

                     存有对象。

                     同样的,基本类型的数组如果是数值型的,就被自动初始化为  0  ; 如果是字符型(char)的,

                     就被自动初始化为 (char)0 ; 如果是布尔型的,就被自动初始化为 false 。

             @@ 数组的 “ 聚集初始化 ” 创建语法

                     例如:

                     Bery []  b = {  new Bery( ) , new Bery( ) , new Bery( )  } ;

》》返回一个数组

             @@  在 Java 中,你写一个方法时可以直接 “ 返回一个数组 ” ,而无需担心要为数组负责-------只要

                     你需要它,它就会一直存在,当你使用完后,垃圾回收器会清理掉它。

             @@   返回一个数组返回任何其他对象实质上是返回引用)没什么区别。           

》》多维数组

             @@   对于基本类型的多维数组,可以通过使用花括号将每个向量分隔开。

                      例如:

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

                     每对花括号括起来的集合都会把你带到下一级数组。

             @@   使用 Java SE5 的  Arrays.deepToString( ) 方法,可以将多维数组转换为多个 String 。

                     --------  Arrays.deepToString( ) 方法对基本类型数组对象数组都起作用。

             @@   自动包装机制对数组初始化器也起作用。

》》数组与泛型

             @@ 通常,数组与泛型不能很好地结合。你不能实例化具有参数化类型的数组:

                     Peel<Banana> [ ]   peels   =  new  Peel<Banana> [10];    // illlegal   

             擦除会移除参数类型信息,而数组必须知道它们所持有的确切类型,以强制保证类型安全。

                    但是,你可以参数化数组本身的类型:

                    class ClassParameter<T>{

                             public  T[ ]   f ( T[ ]  arg )  {

                                     return arg ;

                             }

                    }

             补充:使用参数化方法而不使用参数化类的方便之处在于:你不必为需要应用的每种不同的

                       类型都使用一个参数去实例化这个类,并且你可以将其定义为静态的。

               @@  尽管你不能创建实际的持有泛型的数组对象,但是你可以创建非泛型的数组,然后将其

                      转型。如下:

                      public  static  void main(String[] args){

                               List<String>[]   ls ;              // 这条语句可以顺利地通过编译器而不报任何错误

                               List[ ] la = new  List[10];

                               ls = ( List<String>[] ) la ;     // 转型

                               ls[0] = new ArrayList<String>( ) ;

                       }

                  补充:一旦拥有了对 List<String> 的引用,你就会看到你将得到某些编译器检查。问题是

                   数组是协变类型的,因此  List<String> 也是一个 Object[ ] 。

              @@  一般而言,你会发现泛型在类或方法的边界处很有效,而在类或方法的内部,擦除通常

                 会使泛型变得不适用。

              @@  如果我创建一个 String[ ] ,Java 在编译期运行时都会强制要求我只能将 String 对象

                 置于该数组中。但是,如果创建的是  Object[ ] ,那么我就可以将除基本类型之外的任何对象

                 置于该数组中。 

》》创建测试数据

              @@  通常,在试验数组和程序时,能够很方便地生成填充了测试数据的数组,将会很有帮助。

      ##  Arrays.fill( )

              @@  Java 标准类库 Arrays 有一个十分有限的   fill( ) 方法:只能用同一个值填充各个位置,而

              针对对象而言,就是复制同一个引用进行填充。

              @@  使用 Arrays.fill( ) 可以填充整个数组,只是数组里面都是同一个值。

      ##  数据生成器

              @@ 为了以灵活的方式创建更有意义的数组,引入了  Generator 的概念。

              @@  如果某个工具使用了  Generator , 那么你就可以通过选择 Generator 的类型来创建任何

                      类型的数据(这是策略设计模式的一个实例--------每个不同的 Generator 都表示一个不同的

                      策略)。

      ##   从 Generator 创建数组

              @@ 为了接收  Generator 并产生数组,我们需要两个转换工具。

                     -----第一个工具:使用任意的  Generator 来产生 Object 子类型的数组。

                     -----第二个工具:(为了处理基本类型)接收任意基本类型的包装器类型数组,并产生相应的

                                             基本类型数组。

               @@  所有的 Collection  子类型都拥有  toArray( )  方法,该方法将使用 Collection 中的元素来

                     填充参数数组。

               @@  泛型不能用于基本类型,而我们确实想用生成器来填充基本类型数组。为了解决这个问题,

                      我们创建了一个转换器,它可以接收任意的包装器对象数组,并将其转换为相应的基本类型

                     数组。

》》Arrays  实用功能

               @@ 在 java.util 类库中可以找到 Arrays 类,它有一套用于数组的 static 实用方法,其中有

                   六个基本方法

                    -------- equals( ) 用于比较两个数组是否相等(deepEquals( ) 用于多维数组  )

                    -------- fill( ) 用于向数组中填充值,但是被填充的数组中的值都一样的

                    -------- sort( ) 用于对数组排序

                    --------- binarySearch( ) 用于在已经排序的数组中查找元素

                    --------- toString( ) 用于产生数组的 String 表示

                    --------- hashCode( ) 用于产生数组的散列吗 

                   补充:

                    ---------- 上面的所有方法对各种基本类型和  Object 类而重载过。

                    ----------  Arrays.asList( ) 接受任意的序列数组作为其参数,并将其转变为  List 容器。

      ##  复制数组

                   @@   Java 标准类库提供有 static 方法 System.arraycopy( ) ,用它复制数组比用 for

                   循环要快很多。 System.arraycopy( ) 针对所有类型做了重载。

                    -------   arraycopy( ) 需要的参数有:参数一:源数组

                                                                      参数二: 表示从源数组中的什么位置开始复制的

                                                                                    偏移量

                                                                       参数三:表示从目标数组的什么位置开始复制的

                                                                                    偏移量

                                                                       参数四:需要复制的元素个数

                     ---------  对数组的任何越界操作都会导致异常。

                     --------- System.arraycopy( )  对基本类型数组对象数组都可以复制。然而,如果复制

                               对象数组,那么只是复制了对象的引用----而不是对象本身的拷贝。这被称为

                               浅复制

                     ---------System.arraycopy( )  不会执行自动包装与自动拆包,两个数组必须具有相同的

                              确切类型

      ##  数组的比较

                      @@  Arrays 类提供了重载后的 equals ( ) ,用来比较整个数组。同样,此方法针对所有

                      基本类型与 Object 都做了重载。数组相等的条件是元素个数必须相等,并且对应位置的

                      元素也相等,这可以通过对每一个元素使用 equals( ) 作比较来判断。(对应基本类型,

                      需要使用基本类型的包装器类的  equals( ) 方法,例如,对于 int 类型使用 Integer.equals()

                      作比较 )

      ##   数组元素的比较

                     @@   排序必须根据对象的实际类型执行比较操作。

                     @@   程序设计的基本目标是:将保持不变的事物与会发生改变的事物相分离。而这里,

                      不变的是通用的排序算法变化的是各种对象相互比较的方式

                              因此,不是将进行比较的代码编写成不同的子程序,而是使用策略设计模式。通常使

                       用策略,可以将 “ 会发生变化的代码 ” 封装在单独的类中(策略对象),你可以将策略对象

                       传递给总是相同的代码,这些代码将使用策略来完成其算法。

                              通常上面的策略方式,你能够用不同的对象来表示不同的比较方式,然后将它们传递给

                       相同的排序代码。

                      @@  Java 有两种方式来提供比较功能

                             第一种: 实现 java.lang.Comparable  接口 ,使你的类具有 “天生” 的比较能力。此接口

                                          很简单,只有  compareTo( ) 一个方法。此方法接受另一个 Object 为参数,

                                           如果当前对象小于参数则返回负值,如果相等则返回零,如果当前对象大于参数

                                          则返回正值。

                             第二种:继承自 Object  的  equals( ) 方法

                                          《

                                           补充:如果有特殊的性能的需要,那么可以实现 从Object 继承来的  equals ()  

                                                    方法

                                           》

                      @@  Collections 类包含一个  reverseOrder ( ) 方法,该方法可以产生一个 Comparator ,它

                            可以反转自然的排序顺序。

      ##  数组排序

                     @@   使用内置的排序方法,就可以对任意的基本类型数组排序;也可以对任意的对象数组进行

                      排序,只要该对象实现了 Comparable 接口或具有相关联的  Comparator

                     @@    String  排序算法依据词典编排顺序,所以大写字母开头的词都放在前面输出,然后才是小

                     写字母开头的词。(电话薄通常是这样的。)如果想要忽略大小写字母将单词都放在一起排序,

                     要使用  CASE_INSENSITIVE_ORDER 。

                     @@    Java 标准类库中的排序算法针对正排序的特殊类型进行了优化------针对基本类型设计的

                      “ 快速排序 ”(Quicksort),以及针对对象设计的 “ 稳定归并排序 。所以无需担心排序的性能,

                      除非你可以证明排序部分的确是程序效率的瓶颈。

      ##  在已排序的数组中查找

                     @@   如果数组已经排好序了,就可以使用 Arrays.binarySearch( ) 执行快速查找。如果要对未

                      排序的数组使用 binarySearch( ) ,那么将产生不可预料的结果。

                     @@   如果 Arrays.binarySearch( )  找到了目标,产生的返回值等于或大于 0 。否则,它产生

                      负返回值。

                     @@   如果想要对没有重复元素的数组排序,可以使用   TreeSet(保持排序顺序)或者

                     LinkedHashSet(保持插入顺序),这两个类会自动处理所有的细节。除非细节成为程序性能

                     的瓶颈,否则不需要自己维护数组。

                      @@   如果使用 Comparator 排序了某个对象数组(基本类型数组无法使用 Comparator 进行排序),

                     在使用 binarySearch( ) 时必须提供同样的  Comparator (使用 binarySearch( ) 方法的重载版本)

》》总结

                     @@  当你使用最近的 Java 版本编程时,应该 “ 优选容器而不是数组 ” 。只有在已证明性能成为

                    问题(并且切换到数组对性能提高有所帮助)时,你才应该将程序重构为使用数组。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小达人Fighting

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值