技术开发 频道

深入分析Java中的数据结构

  【IT168 技术文档】是否选择了合适的数据结构进行数据处理对系统的性能有着极大的影响, JDK 中提供了常用的数据结构的实现类,比如链表、堆栈、哈希表,很多第三方开源库也进行了有益的扩展。关于这些类的原理以及使用可以参考相关的手册,在本节中重点讲解一些使用中需要注意的问题 。

  增量内存分配

  ArrayList 、 HashMap 、 Vector 等类都允许我们向其中加入任意多的对象,从而进行处理的,我们在享受它们带来的便利的同时也要注意一定的性能问题。以 ArrayList 为例,我们来看一下在很多情况下是如何编写出低性能的代码的:

  在一个数组中有若干个对象,对象的类型都是 PersonInfo , PersonInfo 的类结构如下:

  public class PersonInfo   {   // 身份证号码   private String id;   // 姓名   private String name;   // 年龄   private int age;   public PersonInfo(String id, String name, int age)   {   super();   this.id = id;   this.name = name;   this.age = age;   }   public int getAge()   {   return age;   }   public String getId()   {   return id;   }   public String getName()   {   return name;   }   }

  请将所有这些 PersonInf 的身份证号码,也就是 id 属性,提取出来,放到另一个 List 类型的变量中。

  实现代码 1 :

  PersonInfo[] persons = new PersonInfo[] {   new PersonInfo("00001", "Tom", 20),   new PersonInfo("00002", "Tim", 23),   new PersonInfo("00003", "Sally", 26),   new PersonInfo("00005", "Lily", 20),   new PersonInfo("00006", "Lucy", 30),   new PersonInfo("00008", "Kitty", 20),   new PersonInfo("00011", "Smith", 20),   new PersonInfo("00031", "Ketty", 22),   new PersonInfo("00051", "Melly", 20),   new PersonInfo("00022", "Blues", 20),   new PersonInfo("00033", "Tid", 18),   new PersonInfo("00101", "Duoliaos", 30),   new PersonInfo("00201", "Yang", 26),   new PersonInfo("03001", "King", 20),   new PersonInfo("05001", "Lee", 20),   new PersonInfo("10001", "Wang", 20),   new PersonInfo("06001", "Pizza", 60) };   List list = new ArrayList();   for (int i = 0, n = persons.length; i < n; i++)   {   PersonInfo pInfo = persons[i];   list.add(pInfo.getId());   }

  程序运行正常,好像没有出现任何问题。程序也确实真的不会出现问题,因为其逻辑如此简单,不会但来问题。但是这个程序性能是最优的吗?

  让我们来看看 ArrayList 类的实现 :

  public class ArrayList extends AbstractList implements List, RandomAccess,   Cloneable, java.io.Serializable   {   ……   private transient Object elementData[];   ……   public ArrayList(int initialCapacity)   {   super();   if (initialCapacity < 0)   throw new IllegalArgumentException("Illegal Capacity: "   + initialCapacity);   this.elementData = new Object[initialCapacity];   }   public ArrayList()   {   this(10);   }   ……   public void ensureCapacity(int minCapacity)   {   modCount++;   int oldCapacity = elementData.length;   if (minCapacity > oldCapacity)   {   Object oldData[] = elementData;   int newCapacity = (oldCapacity * 3) / 2 + 1;   if (newCapacity < minCapacity)   newCapacity = minCapacity;   elementData = new Object[newCapacity];   System.arraycopy(oldData, 0, elementData, 0, size);   }   }   public boolean add(Object o)   {   ensureCapacity(size + 1);   elementData[size++] = o;   return true;   }   }

  正如其名字所暗示的, ArrayList 内部是使用数组保存的数据, Java 中的数组是固定大小的,要想改变数组的大小,就要重新开辟一个新的大小的内存区域,把原先的数据复制到新内存区域中,这就是增量性数组。由于需要重新开辟内存区域并进行数据的复制,因此改变数组的大小是非常耗时的,我们要尽量避免改变数组的大小。

  从 ArrayList 的内部实现我们可以发现, ArrayList 中储存数据用的数组的默认大小为 10 ,当调用 add 方法向其中增加数据的时候,如果数据已经超过了数组的大小, ArrayList 就要增加数组的大小以便容纳更多的数据。在我们的这个人例子中,由于数据已经超过 10 条,当增加到第 11 条的时候, ArrayList 就会进行一次“扩容”,这是一个非常耗时的操作,因此我们必须想方设法避免。

  我们注意到 ArrayList 有一个带参数的构造函数: public ArrayList(int initialCapacity) ,这里的 initialCapacity 代表构造此 ArrayList 的时候,数组的大小。我们可以使用此构造函数来避免“扩容”。

  实现代码 2 :

  PersonInfo[] persons = new PersonInfo[] {   new PersonInfo("00001", "Tom", 20),   new PersonInfo("00002", "Tim", 23),   new PersonInfo("00003", "Sally", 26),   new PersonInfo("00005", "Lily", 20),   new PersonInfo("00006", "Lucy", 30),   new PersonInfo("00008", "Kitty", 20),   new PersonInfo("00011", "Smith", 20),   new PersonInfo("00031", "Ketty", 22),   new PersonInfo("00051", "Melly", 20),   new PersonInfo("00022", "Blues", 20),   new PersonInfo("00033", "Tid", 18),   new PersonInfo("00101", "Duoliaos", 30),   new PersonInfo("00201", "Yang", 26),   new PersonInfo("03001", "King", 20),   new PersonInfo("05001", "Lee", 20),   new PersonInfo("10001", "Wang", 20),   new PersonInfo("06001", "Pizza", 60) };   List list = new ArrayList(persons.length);   for (int i = 0, n = persons.length; i < n; i++)   {   PersonInfo pInfo = persons[i];   list.add(pInfo.getId());   }

  我们已经知道了 list 将放置 persons.length 条数据,因此我们就使 list 中数组的默认大小设置为 persons.length ,这样系统的性能就好了很多。

  不仅是 ArrayList ,我们在使用 Vector 、 HashMap 等类的同时,同样要注意这个问题。

 

0
相关文章