注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

一线天色 天宇星辰

天下武功,唯快不破

 
 
 

日志

 
 

Java常用集合类的注意事项  

2012-04-04 19:56:22|  分类: 软件开发 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

    对于所有开发人员来说,所谓程序就是算法+数据结构,而数据结构与数据存储最常用的就是容器。C++中将核心模版STL分为算法、容器和迭代(主要包含<algorithm><deque><functional><iterator><vector><list><map><memory><numeric><queue><set><stack><utility>16个头文件),其中使用最多的就是容器。Java中使用集合来表示(其实就是容器)。

集合也是Java中经常使用的包,主要有CollectionMap两个接口的实现类构成。其中Collection用于存放多个但对象,而Map则主要用户存放key-value等结构的键值对。

下图展示了JDK中容器的关系图,其中虚线框的表示接口,实线框的表示实现类,粗体框的表示经常用到的容器,如下图所示:

Java常用集合类的注意事项 - 一线天色 天宇星辰 - 一线天色 天宇星辰

 从图中我们可以看出,

Collection又分为ListSet两类数据类型,其中List可以存储重复的元素,而Set不支持

下面我们来逐一看一下这几个类的实现方式。

一、ArrayList

ArrayList是我们日常开发中用到的最多的一个容器,从名字我们就可以看出,它是一个基于数组的容器,我们来看一下它的构造方法的API文档

Java常用集合类的注意事项 - 一线天色 天宇星辰 - 一线天色 天宇星辰

 其构造方法有三个,默认构造方法为创建一个容量为

10的空列表。第二个为通过一个容器创建一个相同大小的数据列表。当传入的数据是另外的容器时,先通过容器的toArray转换为数组,然后通过Arrays.copyOf()方法将传入的数据拷贝到自己的数据容器中。

ArrayList的基本操作(增、删、改、查)用法如下代码示例所示

package com.yhj.container.collection;

 

import java.util.ArrayList;

import java.util.List;

 

/**

 * @Described:列表测试用例

 * @author YHJ create at 2012-4-3 下午04:41:10

 * @FileNmae com.yhj.container.collection.ArrayListTestCase.java

 */

public class ArrayListTestCase {

 

    //主函数

    public static void main(String[] args) {

      

       List<ArrayListTestCase> cases = new ArrayList<ArrayListTestCase>();

       cases.add(null);

       cases.add(null);//可添加null对象,可添加重复数据

       cases.add(new ArrayListTestCase());

       System.out.println(cases);

       cases.add(0, new ArrayListTestCase());

       System.out.println(cases);

       cases.remove(null);//注意:这里是删除第一个为null的对象(根据对象删除)

       System.out.println(cases);

       cases.remove(cases.size()-1);//删除最后一个对象(根据索引删除)

       System.out.println(cases);

       cases.clear();//删除所有元素

       System.out.println(cases);

       cases.add(0,new ArrayListTestCase());

       System.out.println(cases);

       //cases.add(2,new ArrayListTestCase()); //将引发Index: 2, Size: 1下标越界异常

       cases.add(null);

       System.out.println(cases);

       System.out.println("null's index is "+cases.indexOf(null));

       System.out.println(cases.contains(null));

       System.out.println(cases.get(0));

       System.out.println(cases.isEmpty()+"|"+cases.size());

       System.out.println(cases.iterator().hasNext()+"|"+cases.iterator().next());

 

    }

 

}

运行结果如下图所示:

Java常用集合类的注意事项 - 一线天色 天宇星辰 - 一线天色 天宇星辰

 使用

ArrayList注意事项:

1、    ArrayList是基于数组方式实现的一个列表,无容量限制,增长策略为原长度*1.5+1

2、    ArrayList在执行插入时容量不足会扩容,但删除时容量不会减少,如果用户希望容量减少,则可以通过其trimToSize()方法重新整理容量。

3、    ArrayList在查找元素时,对于非null的元算采用equal的方式进行查找。

4、    ArrayList是非线程安全的容器。

二、LinkedList

LinkedList是基于双向链表的一个容器,既可以实现向下移动,也可以实现向上移动。

我们来看以下LinkedList的相关的一些使用方法

package com.yhj.container.collection;

 

import java.util.LinkedList;

import java.util.ListIterator;

 

/**

 * @Described:链表测试用例

 * @author YHJ create at 2012-4-4 下午03:46:20

 * @FileNmae com.yhj.container.collection.LinkedListTestCase.java

 */

public class LinkedListTestCase {

 

    public static void main(String[] args) {

       LinkedList<LinkedListTestCase> cases = new LinkedList<LinkedListTestCase>();

       cases.add(new LinkedListTestCase());

       cases.add(null);

       cases.add(null);

       cases.add(null);

       cases.add(new LinkedListTestCase());

       System.out.println(cases);

       System.out.println(cases.get(0));

       System.out.println(cases.getFirst());

       System.out.println(cases.getLast());

       ListIterator<LinkedListTestCase> iterator = (ListIterator<LinkedListTestCase>) cases.iterator();

       System.out.println(iterator.next()+"|"+iterator.next()+"|"+iterator.previous());

    }

 

}

其运行结果如下:

Java常用集合类的注意事项 - 一线天色 天宇星辰 - 一线天色 天宇星辰

 其他的地方大家可能都明白,但是最后一个地方的

iterator.previous()为什么返回的是null的呢?

其实我们大部分都认为:当执行next的时候,指针从head头节点移动到下一个元素,再次next时,从第一个节点移动到第二个节点。那我此时执行previous返回的是从第二个节点移动到第一个节点时第一个节点的数据,但是不是这样子的,我们来看一下previous的实现代码,如下所示:

    public E previous() {

        if (nextIndex == 0)

       throw new NoSuchElementException();

 

        lastReturned = next = next.previous;

        nextIndex--;

        checkForComodification();

        return lastReturned.element;

    }

很显然,指针位于next.previous这个位置,很显然就是当前的元素的位置喽,也就是第二个元素的数据值,因此是null

使用LinkedList的时候要注意以下几点:

1、  LinkedList是基于双向链表实现的。

2、  LinkedList每次插入时会创建一个新的Entry对象存入,并且换相关指针的引用值,因此容量无上限。

3、  LinkedList查找元算需要从根节点逐个遍历整个链表,直至查到该元素。

4、  LinkedList插入、删除元素都只需要更改对应的指针即可,因此适合增删频繁的场合。

5、  LinkedList是非线程安全的。

三、Vector

VictorArrayList一样,也是基于数组实现的,但不同的是ArrayList默认创建了10个空对象,而Vector是创建0个。另外Vector的增加、删除、查找是加了synchronized关键字的,因此是线程安全的。

Vector的基本用法如下示例代码:

package com.yhj.container.collection;

 

import java.util.Vector;

 

/**

 * @Described:向量测试用例

 * @author YHJ create at 2012-4-4 下午04:44:53

 * @FileNmae com.yhj.container.collection.VectorTestCase.java

 */

public class VectorTestCase {

 

    public static void main(String[] args) {

       Vector<VectorTestCase> cases = new Vector<VectorTestCase>();

       cases.add(null);

       cases.add(null);

       cases.add(null);

       cases.add(null);

       cases.add(null);

       cases.add(new VectorTestCase());

       System.out.println(cases);

       System.out.println(cases.contains(null));

       System.out.println(cases.get(cases.size()-1));

       cases.remove(0);

       System.out.println(cases);

       cases.remove(null);

       System.out.println(cases);

       cases.set(0, new VectorTestCase());

       System.out.println(cases);

       cases.clear();

       System.out.println(cases);

    }

}

运行结果如下:

Java常用集合类的注意事项 - 一线天色 天宇星辰 - 一线天色 天宇星辰

 Vector

使用需要注意以下几点:

1、    Vector相当于加了synchronized关键字的ArrayList

2、    Vector基本上已经被ArrayList取代,如果要线程安全,可自己加同步或者使用Java并发包中的新类CopyOnWriteArrayList

四、Stack

Stack是继承自Vector的实现了后进先出的栈结构。其基本用法如下示例代码:

package com.yhj.container.collection;

 

import java.util.Stack;

 

/**

 * @Described:栈测试 用例

 * @author YHJ create at 2012-4-4 下午04:55:18

 * @FileNmae com.yhj.container.collection.StacktestCase.java

 */

public class StackTestCase {

 

    public static void main(String[] args) {

       Stack<StackTestCase> cases = new Stack<StackTestCase>();

       cases.push(null);

       cases.push(null);

       cases.push(null);

       cases.push(new StackTestCase());

       System.out.println(cases);

       System.out.println(cases.pop());//取值并出栈

       System.out.println(cases.peek());//只取值

       cases.set(0, new StackTestCase());

       System.out.println(cases);

       cases.clear();

       System.out.println(cases);

    }

 

}

其运行结果如下:

Java常用集合类的注意事项 - 一线天色 天宇星辰 - 一线天色 天宇星辰

 Java

栈是线程安全的,我们前面也已经提到,Java大部分操作都是基于栈的操作,可见Java中栈的重要性。

使用Stack要注意以下几点:

1、    Stack是基于Vector实现的一个后进先出的结构。

2、    Stack是线程安全的。

五、HashSet

HashSet是用户存放不重复数据的容器(重复的数据会被后面的值覆盖掉)

HashSet是基于HashMap实现的(HashMap我们在后面的Map中会提到),其基本的操作代码示例如下所示:

package com.yhj.container.collection;

 

import java.util.HashSet;

import java.util.Set;

 

/**

 * @DescribedHashSet测试用例

 * @author YHJ create at 2012-4-4 下午05:03:40

 * @FileNmae com.yhj.container.collection.HashSetTestCase.java

 */

public class HashSetTestCase {

 

    public static void main(String[] args) {

       Set<HashSetTestCase> cases = new HashSet<HashSetTestCase>();

       cases.add(null);

       cases.add(null);

       cases.add(null);

       cases.add(new HashSetTestCase());

       System.out.println(cases);

       cases.remove(null);

       System.out.println(cases);

       cases.add(null);

       System.out.println(cases);

       System.out.println(cases.contains(null));

       cases.clear();

       System.out.println(cases);

       System.out.println(cases.isEmpty());

       System.out.println(cases.iterator());

    }

}

其运行结果如下所示:

Java常用集合类的注意事项 - 一线天色 天宇星辰 - 一线天色 天宇星辰

 使用

HashSet需要注意以下几点:

1、    HashSet是基于HashMap实现,无容量限制。

2、    HashSet是非线程安全的。

六、TreeSet

TreeSetHashSet基本上没有啥区别,唯一的区别就是TreeSet是实现了排序的。其基本用法如下图所示:

package com.yhj.container.collection;

 

import java.util.Set;

import java.util.TreeSet;

 

/**

 * @DescribedTreeSet测试用例

 * @author YHJ create at 2012-4-4 下午05:11:25

 * @FileNmae com.yhj.container.collection.TreeSetTestCase.java

 */

public class TreeSetTestCase implements Comparable<TreeSetTestCase>{

   

    private int treeAge;

 

    public TreeSetTestCase(int treeAge) {

       this.treeAge = treeAge;

    }

 

    @Override

    public int compareTo(TreeSetTestCase o) {

       return this.treeAge-o.getTreeAge();

    }

   

    @Override

    public String toString() {

       return "{age:"+treeAge+";address:"+super.toString()+"}\n";

    }

   

    public static void main(String[] args) {

       Set<TreeSetTestCase> cases = new TreeSet<TreeSetTestCase>();

       cases.add(new TreeSetTestCase(100));

       cases.add(new TreeSetTestCase(50));

       cases.add(new TreeSetTestCase(300));

       System.out.println(cases);

       cases.add(new TreeSetTestCase(10));

       cases.add(new TreeSetTestCase(1000));

       System.out.println(cases);

       System.out.println(cases.remove(new TreeSetTestCase(0)));

       cases.clear();

       System.out.println(cases);

    }

 

    public int getTreeAge() {

       return treeAge;

    }

}

其运行结果如下:

Java常用集合类的注意事项 - 一线天色 天宇星辰 - 一线天色 天宇星辰

 对于

TreeSet,使用时我们应注意以下几点:

1、    TreeSet是基于TreeMap实现的,并且支持排序。

2、    TreeSet是非线程安全的。

七、HashMap

HashMapMap实现类中最常使用到的,其几个构造函数的API如下图所示:

Java常用集合类的注意事项 - 一线天色 天宇星辰 - 一线天色 天宇星辰

 针对

HashMapput方法,

Java常用集合类的注意事项 - 一线天色 天宇星辰 - 一线天色 天宇星辰

 当

keynull是时,HashMap会取Entry数组中的第一个Entry对象,然后逐个遍历,当找到Entry对象的keynull时,替换对应的Entry的值;否则增加一个空的Entry对象。

Key不为null时,先根据key本身取hashcode,然后对keyhashCode再进行hash操作,相关代码为:

    public V put(K key, V value) {

        if (key == null)

            return putForNullKey(value);

        int hash = hash(key.hashCode());

        int i = indexFor(hash, table.length);

        for (Entry<K,V> e = table[i]; e != null; e = e.next) {

            Object k;

            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {

                V oldValue = e.value;

                e.value = value;

                e.recordAccess(this);

                return oldValue;

            }

        }

 

        modCount++;

        addEntry(hash, key, value, i);

        return null;

    }

hash(key.hashCode())的代码为:

    static int hash(int h) {

        // This function ensures that hashCodes that differ only by

        // constant multiples at each bit position have a bounded

        // number of collisions (approximately 8 at default load factor).

        h ^= (h >>> 20) ^ (h >>> 12);

        return h ^ (h >>> 7) ^ (h >>> 4);

    }

indexFor(hash, table.length)的代码为

    static int indexFor(int h, int length) {

        return h & (length-1);

    }

HashMap的基本用法如下代码所示:

package com.yhj.container.map;

 

import java.util.HashMap;

import java.util.Map;

 

/**

 * @DescribedHashMap测试用例

 * @author YHJ create at 2012-4-4 下午05:54:53

 * @FileNmae com.yhj.container.map.HashMapTestCase.java

 */

public class HashMapTestCase {

 

    public static void main(String[] args) {

       Map<String, HashMapTestCase> cases = new HashMap<String, HashMapTestCase>();

       cases.put(null, null);

       cases.put("1", null);

       cases.put("2", new HashMapTestCase());

       System.out.println(cases);

       cases.put("1", new HashMapTestCase());

       System.out.println(cases);

       System.out.println(cases.get("1"));

       System.out.println(cases.containsKey("1")+"|"+cases.containsValue("1"));

       cases.remove("1");

       System.out.println(cases);

       cases.clear();

       System.out.println(cases);

    }

}

运行结果如下:

Java常用集合类的注意事项 - 一线天色 天宇星辰 - 一线天色 天宇星辰

 使用

HashMap需要注意以下注意事项:

1、  HashMap采用数组方式存储keyalue构成的Entry对象,无容量限制;

2、  HashMap采用keyhash算法寻找Entry对象存放的位置,对于冲突的hash采用链表的方式来解决

3、  HashMap在插入元素时有可能要扩大数字的容量,在扩大时会重新计算hash的值并把对应的对象存入新的数组中。

4、  HashMap是非线程安全的。

八、TreeMap

TreeMap是一个支持排序的Map的实现,其实现方式和HashMap不相同,它是先构建一个节点为Root的树根,然后通过红黑树的方式添加数据。其基本用法如下所示:

package com.yhj.container.map;

 

import java.util.Map;

import java.util.TreeMap;

 

/**

 * @DescribedTreeMap测试用例

 * @author YHJ create at 2012-4-4 下午07:08:45

 * @FileNmae com.yhj.container.map.TreeMapTestCase.java

 */

public class TreeMapTestCase {

 

    public static void main(String[] args) {

       Map<String, TreeMapTestCase> cases = new TreeMap<String, TreeMapTestCase>();

       cases.put("3", null);

       //cases.put(null, null); 会引发空指针异常

       cases.put("1", null);

       cases.put("4", null);

        cases.put("5", null);

       cases.put("9", new TreeMapTestCase());

       cases.put("2", null);

       cases.put("6", null);

       cases.put("3", new TreeMapTestCase());

       System.out.println(cases);

       System.out.println(cases.keySet().iterator());

 

    }

 

}

其运行结果如下

Java常用集合类的注意事项 - 一线天色 天宇星辰 - 一线天色 天宇星辰

 使用

TreeMap需要注意的事项如下:

1、  TreeMap是基于红黑树实现的,无容量限制

2、  TreeMap不能使用nullkey

3、  TreeMap是非线程安全的。

说了这么多的容器,那我们实际开发过程中究竟使用哪个才是合适的呢?其实JDK已经为我们做了很多的优化工作,每种容器的性能都差不会太多(仅限于单线程且数据量不超过1w的情况),对于大数据和高并发情况下,JDK5以后为我们提供了一个并发包(下篇博文会专门介绍),这个包里面包含里基本我们所需要的。那我们究竟该选择哪些个容器呢?当然是按需查找。每个容器都提供了不同的功能,你说用哪一个好呢?

 

  评论这张
 
阅读(1962)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017