欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

详解JAVA高质量代码之数组与集合

程序员文章站 2023-12-19 20:38:52
  1.性能考虑,优先选择数组   数组在项目开发当中使用的频率是越来越少,特别是在业务为主的开发当中,首先数组没有list,set等集合提供的诸多方法,查找增加算法都要...

  1.性能考虑,优先选择数组

  数组在项目开发当中使用的频率是越来越少,特别是在业务为主的开发当中,首先数组没有list,set等集合提供的诸多方法,查找增加算法都要自己编写,极其繁琐麻烦,但由于list,set等集合使用泛型支持后,存放的都为包装类,而数组是可以使用基本数据类型,而使用基本数据类型的执行运算速度要比包装类型快得多,而且集合类的底层也是通过数组进行实现.

  2.若有必要,使用变长数组

  在学习集合类当中,很多人喜欢将数组的定长拿来和集合类型的自变长来做比较,但其实这种比较并不合适,通过观察集合类例如arraylist的实现其实可以看出,所谓的集合变长,其实只是用婉转的方式对原数组进行了扩容

  

复制代码 代码如下:

  public static t[] expandcapacity(t[] data, int newlength) {

  // 判断是否为负值

  newlength = newlength < 0 ? 0 : newlength;

  // 生成新数组,拷贝原值并制定长度

  return arrays.copyof(data, newlength);

  }
 


  当性能要求高的时候,可以考虑使用对数组进行封装使用,数组长度不变不是我们不使用它们的借口

  3.警惕数组的浅拷贝

  数组的浅拷贝在java编程中亦是基础中的基础,浅拷贝是在为数组拷贝时,基本类型拷贝的是值,而引用类型拷贝的是引用地址,在上面的例子当中,拷贝数组使用的arrays.copyof为浅拷贝,在使用时需要注意

  4.在明确的场景下,为集合指定初始容量

  在我们平常的使用当中,因为集合类型是自动变长的,所以基本创建对象时不会为集合类附上初始值,就拿我们最常用的arraylist来说明,我们首先要知道,当集合容量到达临界点时,会将底层的数组进行copyof的操作,生成新的数组,而新的数组容量为旧数组的1.5倍,而默认数组长度为10,当我们明确知道要放置入容器中的数据数量较多时,应该指明初始值,避免多次使用copyof造成的性能开销

  5.选择合适的最值算法

  对数据进行最大值或最小值的查找,这是数据结构最基本的知识,在java当中我们亦有很多种的方式进行实现,以下列举2种算法

  

复制代码 代码如下:

  public static int getmaxbyarray(int[] data) {

  // 最简单自行实现的查找方式

  int max = data[0];

  for (int i = 1, size = data.length; i < size; i++) {

  max = max < i ? i : max;

  }

  return max;

  }


复制代码 代码如下:

  public static int getmaxbyarray(int[] data) {

  // 先排序后获取最后位

  arrays.sort(data);

  return data[data.length - 1];

  }


  6.基本类型数组转换陷阱!

  请观察以下代码

复制代码 代码如下:

  public static void main(string[] args) {

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

  list list = arrays.aslist(nums);

  system.out.println(list.size());

  // 此时输出的size为1

  }


  我们期望的结果是将数组中的元素通过arrays.aslist转换到集合类当中,但事与愿违,我们只将数组本身增加了进入,并没有将数组内的值分拆分开来,此时若然对集合list增加了泛型就会在编译期间给出错误的提示,或将数组本身改变成integer就可以解决问题

  7.aslist方法产生的list对象不可更改

  通过上面的例子,我们可以看到使用arrays.aslist方法可以将一个数组转换成一个list,那通过aslist方法返回的list有什么特别呢?注意,这个返回的list是不支持更改的,原因是因为aslist方法返回的,并不是java.util.arraylist,而是arrays工具类中的一个静态私有内部类,虽然都有实现和arraylist一样的父类abstractlist,但在复写add等方法时,却是抛出了unsupportedoperationexception,这个静态私有内部类只实现了size,toarray,get,contains这几个方法

  8.对不同的数据结构使用不同的遍历方式

  请观看以下代码

复制代码 代码如下:

  public static void main(string[] args) {

  // 以下为arraylist集合的遍历方式

  int num = 80 * 10000;

  list arraylist = new arraylist(num);

  for (int i = 0, size = arraylist.size(); i < size; i++) {

  arraylist.get(i);

  }

  // 以下为linkedlist集合的遍历方式

  list linkedlist = new linkedlist();

  for (integer integer : linkedlist) {

  }

  }


  为什么对linkedlist和arraylist要选择不同的遍历方式?

  1.因为arraylist实现了ramdomaccess接口(随机存取接口),ramdomaccess接口和serializable,cloneable接口一样是java中的标示接口,代表这个这个类可以随机存取,对arraylist来说就标志着,数据之间没有关联,即相邻的两个位置没有互相依赖的关系,可以随机访问,

  2.java中的foreach语法是iterator(迭代器)的变形用法,我们知道迭代器是23种设计模式的一种,但迭代器是需要知道两个元素时间的关系的,不然怎么提供hasnext的支持呢?就是因为上一个元素要判断下一个元素是否存在,强行建立了这种关系,违背了arraylist随机存取的特别

  3.在linkedlist中,因为是通过双向链表的形式来存储,所以对迭代器的支持非常好,因为linkedlist相邻的两个元素本来就存在关系所以在对linkedlist和arraylist要采取不同的遍历方式,读者若然有兴趣可以尝试一下对linkedlist采用下标的形式访问,会发现两者的效率有较大的差距

  8.适时选择arraylist或linkedlist

  arraylist和linkedlist的主要区别:

  1.arraylist底层的数据结构为数组,而linkedlist底层结构为双向链表

  2.在插入数据时,由于arraylist每次插入后都需要将数组元素向后顺延位置,而linkedlist只需要更改头节点和尾节点即可完成插入操作,所以在插入操作较为频繁时,优先使用linkedlist

  3.在删除数据时,由于arraylist要保持数组的有序性,当删除后元素要亦需要向后或向前移位,而linkedlist照旧还是更改头尾节点.

  4.在更新时,由于linkedlist会使用折半遍历的方式进行查找定位元素再进行更新,对比起arraylist的直接定位下标元素替换,arraylist对更新的效率更佳

  5.linkedlist可以模拟队列,通过linkedlist的addfirst,addlast等操作

  9.列表相等只需关心元素数据

  java为了我们可以安心的面向list,set,map等接口进行编程,因此对集合类中的equlas进行了复写,让我们在比较两个集合是否相等时,只需要比较元素数据是否相等即可,避免了因为替换集合实现类造成的错误java代码

复制代码 代码如下:

  public static void main(string[] args) {

  list arraylist = new arraylist();

  arraylist.add(1);

  arraylist.add(2);

  list linkedlist = new linkedlist();

  linkedlist.add(1);

  linkedlist.add(2);

  system.out.println(arraylist.equals(linkedlist));

  // 不用关心具体实现,输出为true

  }

上一篇:

下一篇: