`

Java 集合类

 
阅读更多

Java 集合类

 

 

1. 为什么要了解Java 集合类

Java 集合类提供了如线性表,链表和哈希表等基础数据结构的实现,通过它可以实现各种你想要的数据结构,如stack ,queue 等,了解Java 集合类的工作原理可以编出更高效性能的程序,另外了解其工作原理可以更好地使用它,避免因为滥用而出现性能上的问题。事实证明,很多性能上的问题都是因为使用不当引起的。

 

2.Java 集合类的概述

2.1 Java collection 的继承关系

 

Collection   
├List   
│├LinkedList   
│├ArrayList   
│└Vector   
│ 
  └Stack   
└Set  

└SortedSet

└HashSet

└TreeSet


Map   
├Hashtable   
├HashMap 

├SortedMap

  └TreeMap  
└WeakHashMap 

2.2 Java collection 概述

2.2.1 List 的简要概述

Java2 的集合框架,抽其核心,主要有三种: List 、 Set 和 Map

 

1. List 的主要特性

1) 有序的 Collection

2) 使用该接口可以精确定义元素插入的位置。

3) 用户能够通过索引来访问 List 的元素。

4) 元素允许重复,允许有 null 元素,基于 array 的 list 适合查询, linkedList 适合添加删除操作。

 

2. List 的主要方法

boolean contains(Object o);

Iterator<E> iterator();

Object[] toArray();

boolean add(E e)

boolean remove(Object o);

boolean containsAll(Collection<?> c);

boolean addAll(Collection<? extends E> c);

boolean addAll(int index, Collection<? extends E> c);

boolean removeAll(Collection<?> c);

boolean retainAll(Collection<?> c);

void clear();

E get(int index);

E set(int index, E element);

void add(int index, E element);

E remove(int index);

int indexOf(Object o);

int lastIndexOf(Object o);

 

2.Set 是不含重复元素的、无序的 Collection

 

3.Map 是一种以 key 和 value 形成对应关系的容器, key 重复会覆盖 value

 

2.2.2 Java collection  fail-fast 机制

在我们软件开发的过程中,当碰到问题了,我们第一步就是重现问题,然后进行 debug ,找出问题的原因,但往往有些 bug 隐藏的比较深,经过 n 次特定的操作的在特定的环境下才会重现,或者出错后,报错的 stack traces的没有指出错误的真正起点。 
所以我们设计一个 Fail Fast 的系统显得很重要了。 
很多人设计的系统,为了增强系统的健壮性,自动的将系统中出现的问题处理掉,并继续运行,但是在未来的某个时间引发奇怪的错误。 
而 fail fast 的系统的做法却是相反的,当有问题发生,立即出错,并将出错信息显示出来。这样 bug 就很容易被发现,而不会进入到最终的产品中。

java.util 包中的集合类都返回 fail-fast 迭代器,这意味着它们假设线程在集合内容中进行迭代时,集合不会更改它的内容。如果 fail-fast 迭代器检测到在迭代过程中进行了更改操作,那么它会抛出ConcurrentModificationException ,这是不可控异常。

 

import java.util.ArrayList;

import java.util.Iterator;

import java.util.List;

 

public class TestFastFail {

   

    public static void main(String[] args) {

       List<String> list = new ArrayList<String>(20);

       for ( int i = 0; i < 20; i++) {

           list.add( "test" );

       }

      

        // 在迭代器迭代过程中,如果集合被修改被抛出 java.util.ConcurrentModificationException

       // 这就是 fast fail 的意义,早点发现程序的错误,并抛出异常

       Iterator<String> iterator = list.iterator();

       for ( int i = 0; iterator.hasNext(); i++) {

           String string = iterator.next();

           System. out .println(string);

           list.add( "" );

       }

    }

}

分享到:
评论
4 楼 csdn_zuoqiang 2012-06-07  
CopyOnWriteArrayList工作原理和实例

CopyOnWriteArrayList顾名思义,在写入操作时,copy源数组到新的数组中,而读取时,是从源数组去读的,因为写入操作是在另外一个数组中执行,因此在读取时,不用进行线程同步,但是要注意一点,copy数组的开销在数据量大的情况下,非常耗资源,因此,它的使用场景,适合于读取远大于写入操作的场景。当然,在写入时,是有锁的,JDK中的实现是采用重入显式锁进行锁定的。当写操作完成以后再将源数组的引用指向copy的数组,最后释放锁。

主要方法如下:
public boolean add(E e)
  public void add(int index, E element)
public E get(int index)
public E set(int index, E element)
remove(i);
public int lastIndexOf(Object o)
public boolean addIfAbsent
3 楼 csdn_zuoqiang 2012-06-07  
这段时间好好整理了一下基础,发现很多对我来说新的东西,里面博大精深的东西真的很多,经常使用HashMap,对HashMap的结构和原理非常了解,但是忽略了还有LinkedHashMap这个好东西。
 
先转一篇blog:
 
LinkedHashMap的特性:
Linked内部含有一个private transient Entry header;来记录元素插入的顺序或者是元素被访问的顺序。利用这个线性结构的对象,可以帮助记录entry加入的前后顺序或者记录entry被访问的 频率(最少被访问的entry靠前,最近访问的entry靠后)。大致的过程如下:

new LinkedHashMap(10, 0.75, true);
其中前面两个参数就是HashMap构造函数需要的参数,后面的true表明LinkedHashMap按照访问的次序来排序。
按照访问的次序来排序的含义:当调用LinkedHashMap的get(key)或者put(key, value)时,碰巧key在map中被包含,那么LinkedHashMap会将key对象的entry放在线性结构的最后。
按照插入顺序来排序的含义:调用get(key), 或者put(key, value)并不会对线性结构产生任何的影响。

正是因为LinkedHashMap提供按照访问的次序来排序的功能,所以它才需要改写HashMap的get(key)方法(HashMap不需要排序)和HashMap.Entry的recordAccess(HashMap)方法
public Object get(Object key) {
        Entry e = (Entry)getEntry(key);
        if (e == null)
            return null;
        e.recordAccess(this); 
        return e.value;
    }

void recordAccess(HashMap m) {
            LinkedHashMap lm = (LinkedHashMap)m;
            if (lm.accessOrder) {
                lm.modCount++;
                remove();
                addBefore(lm.header);
            }
        }
注 意addBefore(lm.header)是将该entry放在header线性表的最后。(参考LinkedHashMap.Entry extends HashMap.Entry 比起HashMap.Entry多了before, after两个域,是双向的)

至于put(key, value)方法, LinkedHashMap不需要去改写,用HashMap的就可以了,因为HashMap在其put(key, value)方法里边已经预留了e.recordAccess(this);

还有一个方法值得关注:
    protected boolean removeEldestEntry(Map.Entry eldest) {
        return false;
    }
当 调用put(key, value)的时候,HashMap判断是否要自动增加map的size的作法是判断是否超过threshold, LinkedHashMap则进行了扩展,如果removeEldestEntry方法return false;(默认的实现),那么LinkedHashMap跟HashMap处理扩容的方式一致;如果removeEldestEntry返回 true,那么LinkedHashMap会自动删掉最不常用的那个entry(也就是header线性表最前面的那个)。
这会造成严重的性能问题吗?答案当然是否定的。因为在这儿的链表操作是常量级的。这也是LinkedHashMap/Set在这儿比TreeMap/Set性能更高的原因。
同样,LinkedHashMap/Set也不是thread-safe的。如果在多线程下访问,是需要进行外部同步,或者使用Collections.synchronizedMap()的方法包装成一个thread-safe的Map/Set。
特别需要注意的是,在使用“访问顺序”时,读取节点操作也是“结构变化”的操作。因为,这会改变元素遍历的顺序。所以,在使用 LinkedHashMap的iterator()方法,遍历元素时,如果其它线程有读取操作,也要进行同步。否则,也会抛出同其它fail-fast一 样的由于删除或增加操作而引起的CurrentModificationException的例外。 
最后,LinkedHashMap缺省是使用插入顺序的,如何构造一个访问顺序的LinkedHashMap呢?很简单: public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) accessOrder = true 即可。
 
回来补充一个利用LinkedHashMap来实现LRU的Cache类,看了上面的特性,实现起来实在太简单了!
 
package lru;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;

/**
 *
 *<p>Test</p>
 *<p>Description:</P>
 *<p>Company:Cisco CAS</p>
 *<p>Department:CAS</p>
 *@Author: Tommy Zhou
 *@Since: 1.0
 *@Version:Date:2011-5-13
 *
 **/

public class LRUCache<K,V> extends LinkedHashMap<K, V>{
    private LinkedHashMap<K,V>  cache =null ;
    private int cacheSize = 0;
       
    public LRUCache(int cacheSize){
        this.cacheSize = cacheSize;
        int hashTableCapacity = (int) Math.ceil (cacheSize / 0.75f) + 1;
        cache = new LinkedHashMap<K, V>(hashTableCapacity, 0.75f,true)
        {
            // (an anonymous inner class)
            private static final long serialVersionUID = 1;

            @Override
            protected boolean removeEldestEntry (Map.Entry<K, V> eldest)
            {
                System.out.println("size="+size());
                return size () > LRUCache.this.cacheSize;
            }
        };
    }
   
    public V put(K key,V value){
        return cache.put(key, value);
    }

    public V get(Object key){
        return cache.get(key);
    }
    
    public static void main(String[] args) {
        LRUCache<String, String> lruCache = new LRUCache<String, String>(5);
        lruCache.put("1", "1");
        lruCache.put("2", "2");
        lruCache.put("3", "3");
        lruCache.put("4", "4");
        
        System.out.println(lruCache.get("2"));
        lruCache.get("2");
        lruCache.put("6", "6");
        lruCache.put("5", "5");
        System.out.println(lruCache.get("1"));
        
        
        
        
    }

}
2 楼 csdn_zuoqiang 2012-06-07  
4.  Set
Set 是不包含重复元素的集合。可以更加正式地表达为,在 set 里面的元素 e1,e2, 不允许 e1.equals(e2), 而且最多有一个 null 元素。
Set 的主要方法如下,可以看出 List 和 Set 在方法上的区别了, List 能够根据类似于 index ,来找到元素的方法,而 set 只管往集合里面放东西,并不管其插入的顺序, List 有比较严格的插入顺序。
//Set 是否为空
boolean isEmpty();
// 是否包含指定元素
boolean contains(Object o);
// 遍历 Set
Iterator<E> iterator();
// 转化成数组
Object[] toArray();
// 添加元素
boolean add(E e);
// 清空集合
void clear();
// 只保留包含指定集合的元素
boolean retainAll(Collection<?> c);

4.1 HashSet
1 . HashSet 的数据结构和工作原理
HashSet 的基本数据结构是 HashMap , HashMap 的基本数据结构是哈希表和链表,在构造函数上可以参见 HashMap 的工作原理,和 HashMap 一样,影响性能的因素包括: set 的初始化容量大小,容量因子,和 HashCode 的构造,其主要方法实现如下:
/**
* 添加元素,注意是将 elment 作为 key 存进 HashupMap ,
* 同时指定所有对应的 element 的 value 都为同一个对象,
* 这样可以防止元素重复,同时可以看出 Hashset 允许
* 空元素
*/
public boolean add(E e) {
       return map.put(e, PRESENT)==null;
}
/**
* 移除元素
*/
public boolean remove(Object o) {
       return map.remove(o)==PRESENT;
}
/**
* 清空 set
*/
public void clear() {
       map.clear();
}
 
/**
*Clone Set
*/
public Object clone() {
       try {
              HashSet<E> newSet = (HashSet<E>) super.clone();
              newSet.map = (HashMap<E, Object>) map.clone();
              return newSet;
       } catch (CloneNotSupportedException e) {
              throw new InternalError();
       }
}
 
public boolean contains(Object o) {
       return map.containsKey(o);
}
 
public boolean isEmpty() {
       return map.isEmpty();
}
public int size() {
       return map.size();
}
 
public Iterator<E> iterator() {
       return map.keySet().iterator();
}
1 楼 csdn_zuoqiang 2012-06-07  
3. List
3.1 Vector
1 . Vector 的数据结构和工作原理
Vector 是基本数据结构一个可变数组,数组的元素可以是任意对象,当 Vector 初始创建时,会创建一个一定容量(Capacity)的(默认为 10 )数组对象,当添加的对象超过数组的容量时, Vector 在内部会重新创建一个新的数组,再把原数组元素拷贝到新的数组里面,这样就完成了动态数组的功能。 Vector 的构造器有三种
1) public Vector(int initialCapacity, int capacityIncrement)
initialCapacity 是数组的初始化长度, capacityIncrement 是当数组长度超过原长度时,数组长度增长的步长, capacityIncrement 为 0 时,此种情况当数组长度超过原长度峰值时,此时数组长度变为原来的 2 倍。当数组长度变化,原有的数据和新的数组对象是通过 copy 来完成的。
2) public Vector(int initialCapacity)
3) public Vector()
Vector 默认构造器的数组的长度是 10
 
 
核心方法:
/**
     * 核心方法之一:保证数组的容量,如果没有指定容量的增长的步长,则将原数组容量扩 * 大成原来的两倍,如果指定则按照增长的步长增加容量,并且取 minCapacity 和 *newCapacity 计算出来的最大值进行取值。计算出来的最终容量将作为新数组对象的尺 * 寸,并将原来数组的元素 copy 到新数组里面来。
     *
     * @see #ensureCapacity(int)
     */
    private void ensureCapacityHelper( int minCapacity) {
    int oldCapacity = elementData . length ;
    if (minCapacity > oldCapacity) {
        Object[] oldData = elementData ;
        int newCapacity = ( capacityIncrement > 0) ?
       (oldCapacity + capacityIncrement ) : (oldCapacity * 2);
         if (newCapacity < minCapacity) {
       newCapacity = minCapacity;
        }
            elementData = Arrays.copyOf ( elementData , newCapacity);
    }
    }
 
2.Vector 使用时需要注意的问题
1) Vector 使用时,最好能够预期需要存放元素的个数,这样可以避免数组长度频繁的改变而引起数据的 copy 带来的开销,同时会引起内存大小的增加。
2 )使用 Vector 需要知道 Vector 是线程安全的。因此,在使用时,最好是考虑这个对象会不会因为多个线程访问该 Vector 对象,如果确认只有单个线程访问,则可以用 ArrayList 来代替 Vector 。
 
 
3.    Stack
有 Vector 可以轻松改造成 Stack ,栈的特点是先进后出,对于一个栈来说,主要方法包括, push(E e), pop, peek,empty 和 search(E e).
public class Stack <E> extends Vector<E> {
   
    public Stack(){
       super (10);
    }
   
    public void push(E e){
       this .add(e);
    }
   
    public E pop(){
       return this .remove( this .size()-1);
    }
   
    public boolean empty(){
       return this .size()==0;
    }
   
    public int search(E e){
        for (int i = 0; i < this.size(); i++) {
            if(this.elementAt(i).equals(e)){
                return i;
            }else{
                continue;
            }
        }
        
        return -1;
    }
}

相关推荐

    java集合类详解(set list ArrayList等java集合类详述)

    Java 集合类详解 Java 集合类是 Java 语言中的一种基本数据结构,用于存储和操作大量数据。集合类可以分为三大类:Collection、List 和 Set。 Collection 是集合框架中的根接口,提供了基本的集合操作,如 add、...

    Java集合排序及java集合类详解.pdf

    ### Java集合排序及Java集合类详解 #### 一、集合框架概述 集合框架是Java编程语言的核心组件之一,用于组织和操作数据集。Java集合框架提供了多种数据结构,包括列表(List)、集(Set)和映射(Map),这些数据结构...

    第13讲 JAVA集合类.ppt

    Java集合类是Java编程语言中用于存储和管理对象的关键组件,它们构成了Java Collections Framework的核心。这个框架提供了一组高效、灵活的数据结构,使得开发者能够轻松地处理数据集合,而无需关心底层实现的复杂性...

    java集合类线程安全.doc

    所涉及的集合类不仅包括 Java SE 1.2 引入的集合类,还包括旧集合类(Java SE 1.2 前引入)和新集合类(Java SE 5 引入)。 Java 线程安全的等级定义根据 Bloch 的定义,将线程安全分为五个等级: 1. 非可变:如果...

    一张图让你看清Java集合类

    一张图让你看清Java集合类 所有精华 集于一图 一目了然 形象易懂 十分中肯 绝对干货!

    大公司最喜欢问的Java集合类面试题

    ### Java集合类重要知识点 #### 一、概述 在Java编程中,集合类是一个非常重要的概念,主要用于存储和管理对象的集合。Java集合框架主要包括两大类:`Collection`和`Map`。本篇文章将着重介绍`Collection`部分,并...

    Java集合类性能分析

    ### Java集合类性能分析 #### 一、Java集合框架概览 Java集合框架是一个非常重要的概念,它提供了处理数据集合的标准方法。集合框架的核心部分主要包括集合接口、抽象类以及具体的实现类。 - **集合接口**:Java...

    java集合类详解

    Java集合类是Java语言中用来存储数据的结构,它们是Java开发中非常重要的组件。在Java 2平台之前,集合框架的组成较为零散,自Java 2平台的JDK 1.2版本之后,引入了集合框架(Collections Framework),为集合类提供...

    java 集合类 容器类

    ### Java集合类与容器类详解 #### 一、引言 在Java编程中,集合类是一种非常重要的数据结构,用于存储一系列对象。相比于数组,集合类提供了更多的灵活性和功能,尤其是在处理未知数量的对象时更为方便。Java标准...

    java集合类面试题总结

    Java 集合类面试题总结 Java 集合类是 Java 语言中的一种重要组件,用于存储和操作数据。下面总结了 Java 集合类的一些常见问题和答案。 HashMap 和 Hashtable 的区别 HashMap 和 Hashtable 都是 Java 中的散列表...

    Java 集合排序及java 集合类详解

    Java 集合排序及java 集合类详解 Java 集合排序及java 集合类详解,Java里面最重要、最常用也就是集合那部分了,能够用好集合和理解好集合对于做Java程序的开发拥有无比的好处。本教程详细解释了关于Java中的集合是...

    Java集合类图片

    Java集合类,在图片上体现出来,为了更好的描述,本来是博客里的,不好往博客里插,所以单独弄出来了。

    Java集合类矩阵图

    Java集合类矩阵图是Java编程中非常重要的一个概念,它主要涵盖了Java集合框架中的各种接口、类以及它们之间的关系。这个矩阵图可以帮助开发者更清晰地理解Java集合框架的层次结构和实现方式。在这个矩阵图中,你可以...

    java集合类精华大全

    Java集合类是Java编程中非常重要的一个部分,它为开发者提供了灵活的数据结构,用来存储和管理数据。在Java中,集合类主要分为三大类:Set、List和Map,它们都位于`java.util`包中。 Set接口表示无序且不允许重复...

    Java 集合类 简单Demo

    本示例主要探讨的是Java集合类的简单使用,通过一个名为`CollectionsTest.java`的文件进行演示。这篇博客文章可能详细解释了如何创建、操作和理解Java集合类的基本概念。 首先,Java集合框架主要包括接口和实现这些...

    java集合分类总结.doc

    Arrays和Collections是Java集合中的两个工具类。Arrays类包含用来操作数组的各种方法,如排序和搜索等。Collections类主要提供了在collection上进行操作的方法,如排序、查找等。 学习Java集合需要掌握以下几个方面...

    java 集合类讲解

    Java集合类是Java编程语言中一个非常重要的概念,它提供了数据结构和算法的实现,使得开发者可以方便地存储、管理和操作对象。Java集合框架包括多种接口(如List、Set、Queue)和实现这些接口的类(如ArrayList、...

    java集合类演示源码

    集合类的框架为集合的实现者提供了大量的接口和抽象类,并对其中的某些机制给予了描述,例如,Iterator(迭代协议)。实现Comparable接口或Comparator接口,用户可以根据需要对集合中的元素进行排序。为了方便用户...

Global site tag (gtag.js) - Google Analytics