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

  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

  }

(0)

相关推荐

  • java 用泛型参数类型构造数组详解及实例

    java 用泛型参数类型构造数组详解及实例 前言: 前一阵子打代码的时候突然想到一个问题.平时我们的数组都是作为一个参数传入方法中的,如果我们要想在方法中创建一个数组怎么样呢?在类型明确的情况下,这是没什么难度的.如果我们传入的参数是泛型类型的参数呢? public static <T> T[] creArray (T obj){ T[] arr = new T[10]; } 像上面这种用T来直接new数组的方法是错误的,会编译时出现一个:Cannot create a generic arr

  • java判定数组或集合是否存在某个元素的实例

    引言: 今天群里有朋友问"怎么知道一个数组集合是否已经存在当前对象",大家都知道循环比对,包括我这位大神群友.还有没其他办法呢?且看此篇. 正文: 能找到这里的都是程序员吧,直接上代码应该更清楚些. import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.regex.Matcher; im

  • Java集合和数组的区别

    集合和容器都是Java中的容器. 区别 数组特点:大小固定,只能存储相同数据类型的数据 集合特点:大小可动态扩展,可以存储各种类型的数据 转换 数组转换为集合: Arrays.asList(数组) 示例: int[] arr = {1,3,4,6,6}; Arrays.asList(arr); for(int i=0;i<arr.length;i++){ System.out.println(arr[i]); } 集合转换为数组: 集合.toArray(); 示例: List list = ne

  • java数组、泛型、集合在多态中的使用及对比

    我们在使用数组,泛型集合的过程中不可避免的会碰到多态,或者说什么情况下能如何使用父数组引用子数组(集合.泛型)呢? 数组在多态中的使用 元素为父类型的数组引用可指向元素为子类型的数组对象 当数组被调用时由当前引用确定数组元素的类型 当元素为父类型的数组引用指向了元素为子类型的数组对象时,不可通过父类型的数组引用向集合加入除子类型及其下层级的之外的元素(可通过编译,运行过程出错) /*验证: *animals和dogs可以引用同一个数组 *当animals引用数组时,数组元素为Animal类型 *

  • Java中集合和数组的排序方式小结

    根据约定,在使用java编程的时候应尽可能的使用现有的类库,当然你也可以自己编写一个排序的方法,或者框架,但是有几个人能写得比JDK里的还要好呢?使用现有的类的另一个好处是代码易于阅读和维护,这篇文章主要讲的是如何使用现有的类库对数组和各种Collection容器进行排序,(文章中的一 部分例子来自<Java Developers Almanac 1.4>) 首先要知道两个类:java.util.Arrays和java.util.Collections(注意和Collection的区 别)Co

  • java实现列表、集合与数组之间转化的方法

    本文实例讲述了java实现列表.集合与数组之间转化的方法.分享给大家供大家参考.具体实现方法如下: 复制代码 代码如下: package test;  import java.util.ArrayList;  import java.util.Arrays;  import java.util.HashSet;  import java.util.List;  import java.util.Set;  public class Test2 {      public static void

  • 浅谈java中集合的由来,以及集合和数组的区别详解

    对象多了用集合存,数据多了用数组存. 数组是固定长度的,集合是可变长度的. 集合是:只要是对象就可以存,不管是不是同一种对象 而数组只能存储一种类型的对象 下面是集合的框架: 以上就是小编为大家带来的浅谈java中集合的由来,以及集合和数组的区别详解的全部内容了,希望对大家有所帮助,多多支持我们~

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

    1.性能考虑,优先选择数组 数组在项目开发当中使用的频率是越来越少,特别是在业务为主的开发当中,首先数组没有List,Set等集合提供的诸多方法,查找增加算法都要自己编写,极其繁琐麻烦,但由于List,Set等集合使用泛型支持后,存放的都为包装类,而数组是可以使用基本数据类型,而使用基本数据类型的执行运算速度要比包装类型快得多,而且集合类的底层也是通过数组进行实现. 2.若有必要,使用变长数组 在学习集合类当中,很多人喜欢将数组的定长拿来和集合类型的自变长来做比较,但其实这种比较并不合适,通过观

  • 详解Java高并发编程之AtomicReference

    目录 一.AtomicReference 基本使用 1.1.使用 synchronized 保证线程安全性 二.了解 AtomicReference 2.1.使用 AtomicReference 保证线程安全性 2.2.AtomicReference 源码解析 2.2.1.get and set 2.2.2.lazySet 方法 2.2.3.getAndSet 方法 2.2.4.compareAndSet 方法 2.2.5.weakCompareAndSet 方法 一.AtomicReferen

  • 详解Java高阶语法Volatile

    背景:听说Volatile Java高阶语法亦是挺进BAT的必经之路. Volatile: volatile同步机制又涉及Java内存模型中的可见性.原子性和有序性,恶补基础一波. 可见性: 可见性简单的说是线程之间的可见性,一个线程修改的状态对另一个线程是可见对,也就是一个线程的修改结果另一个线程可以马上看到:但通常,我们无法确保执行读操作的线程能够时时的看到其他线程写入的值,So为了确保多个线程之间对内存写入操作可见性,必须使用同步机制:如用volatile修饰的变量就具有可见性,volat

  • 详解如何把Java中if-else代码重构成高质量代码

    为什么我们写的代码都是if-else? 程序员想必都经历过这样的场景:刚开始自己写的代码很简洁,逻辑清晰,函数精简,没有一个if-else, 可随着代码逻辑不断完善和业务的瞬息万变:比如需要对入参进行类型和值进行判断:这里要判断下对象是否为null:不同类型执行不同的流程. 落地到具体实现只能不停地加if-else来处理,渐渐地,代码变得越来越庞大,函数越来越长,文件行数也迅速突破上千行,维护难度也越来越大,到后期基本达到一种难以维护的状态. 虽然我们都很不情愿写出满屏if-else的代码,可逻

  • 详解Java中数组判断元素存在几种方式比较

    1. 通过将数组转换成List,然后使用List中的contains进行判断其是否存在 public static boolean useList(String[] arr,String containValue){ return Arrays.asList(arr).contains(containValue); } 需要注意的是Arrays.asList这个方法中转换的List并不是java.util.ArrayList而是java.util.Arrays.ArrayList,其中java.

  • 详解Java Fibonacci Search斐波那契搜索算法代码实现

    一, 斐波那契搜索算法简述 斐波那契搜索(Fibonacci search) ,又称斐波那契查找,是区间中单峰函数的搜索技术. 斐波那契搜索采用分而治之的方法,其中我们按照斐波那契数列对元素进行不均等分割.此搜索需要对数组进行排序. 与二进制搜索不同,在二进制搜索中,我们将元素分成相等的两半以减小数组范围-在斐波那契搜索中,我们尝试使用加法或减法来获得较小的范围. 斐波那契数列的公式是: Fibo(N)=Fibo(N-1)+Fibo(N-2) 此系列的前两个数字是Fibo(0) = 0和Fibo

  • 详解Java中一维、二维数组在内存中的结构

    前言 我们知道在Java中数组属于引用数据类型,它整个数组的数组元素既可以是基本数据类型的(如 byte \ int \ short \ long \ float \ double \ char \ boolean 这些),也可以是引用数据类型的.当它的数组元素是基本数据类型时,这个数组就是一个一维数组:当它的数组元素是引用数据类型时,它就是一个多维数组.比如,在一个数组中它的某个元素值其实是一个一维数组,而其他不同的元素也各自包含了一个一维数组,我们就把这个包含很多个一维数组的数组叫做二维数组

  • 详解Java数组的四种拷贝方式

    目录 深拷贝与浅拷贝的区别 1.for循环进行拷贝 拷贝数值类型 拷贝引用类型 2.copyof/copyOfRange 拷贝数值类型 拷贝引用类型 3.arraycopy 拷贝数值类型 拷贝引用类型 4.clone 拷贝数值类型 拷贝引用类型 5.总结 深拷贝与浅拷贝的区别 假设现在有原数组A以及拷贝后的数组B,若是改变A中的某一个值,B数组随之相应的发生变化的拷贝方式称为浅拷贝,反之B数组不受影响,则称为深拷贝:简单总结一下两者的概念: 深拷贝:拷贝后,修改原数组,不会影响到新数组: 浅拷贝

  • 详解Java前缀树Trie的原理及代码实现

    目录 Trie的概念 Trie的实现 基本结构 构建Trie 查找字符串 Trie的总结 Trie的概念 Trie(发音类似 “try”)又被称为前缀树.字典树.Trie利用字符串的公共前缀来高效地存储和检索字符串数据集中的关键词,最大限度地减少无谓的字符串比较,其核心思想是用空间换时间. Trie树可被用来实现字符串查询.前缀查询.词频统计.自动拼写.补完检查等等功能. Trie树的三个性质: 根节点不包含字符,除根节点外每一个节点都只包含一个字符. 从根节点到某一节点,路径上经过的字符连接起

  • 详解Java读取Jar中资源文件及示例代码

    详解Java读取Jar中资源文件及实现代码 直接上代码,文章的注释部分说的比较清楚,大家可以参考下, 工具类源代码: ResourceLoadFromJarUtil.java 实现代码: import java.io.IOException; import java.io.InputStream; import java.net.JarURLConnection; import java.net.MalformedURLException; import java.net.URL; import

随机推荐