Java--集合

1.集合的总体概括

Java集合主要可以划分为4个部分:List列表、Set集合、Map映射、工具类(Iterator迭代器、Enumeration枚举类、Arrays和Collections)、。
Java集合工具包框架图(如下):

2.关于Collection中的List

public class ListTest {
    /*
     * ArrayList 是一个数组队列,相当于动态数组。它由数组实现,随机访问效率高,随机插入、随机删除效率低。   
     * LinkedList是一个双向链表。它也可以被当作堆栈、队列或双端队列进行操作。LinkedList随机访问效率低,但随机插入、随机删除效率高。  
     * Vector是矢量队列,和ArrayList一样,它也是一个动态数组,由数组实现。但是ArrayList是非线程安全的,而Vector是线程安全的。
     *  Stack是栈,它继承于Vector。它的特性是:先进后出(FILO, First In Last Out)。
     */
    static ArrayList list = new ArrayList();
    static LinkedList list2 = new LinkedList();
    static Vector list3 = new Vector(3, 4);
    static Stack list4 = new Stack();

    public static void main(String[] args) {
        /*
         * ArrayList是索引动态数组,当删除一个数时,后面的覆盖前面的,并且size-1 ,随机访问快,直接获取其索引值,插入需要
         * 扩充其容量,删除需要数组复制,即后面的覆盖前面的 故慢
         * LinkedList是个双向链表,采用二分查找法进行随机访问和插入删除,故随机访问慢,插入删除快
         * ArrayList支持序列化,而Vector不支持
         * 即ArrayList有实现java.io.Serializable接口,而Vector没有实现该接口。
         * Vector有一个构造函数可以指定容量增加系数。
         */
        list3.add("1");
        list3.add("2");
        list3.add("3");
        list3.add("4");
        list3.add("5");
        list3.add("6");
        list3.add("7");
        System.out.println(list3.size());
        //list4.remove(0);
        //list4.remove(1);
        //list4.remove(2);
        //list4.remove(3);
        System.out.println(list3);
        /*
         * 测试vector的容量增加
         * 构造函数是可以指定容量的(3,10)3+10*n
         * .setSize()是可以重新指定vector的容量大小的,这个容量就是固定的了
         */
        list3.setSize(60);
        System.out.println(list3.capacity());
    }

}

3.关于Collection的映射接口Map

public class MapTest {
    /* 拉链法是解决哈希冲突的,当不同的key计算得到相同的hashcode,则创建列表将其插在最后,形成链表,即Map中的一个桶
     * 散列表是哈希表,是有相同hashcode的链表的集合
     * 强键和弱键的区别:除指向自身的引用外,当指针指向null,弱键会被GC回收
     * 
     * 动态回收:
     * (01) 新建WeakHashMap,将“键值对”添加到WeakHashMap中。将“键值对”添加到WeakHashMap中时,添加的键都是弱键。
        实际上,WeakHashMap是通过数组table保存Entry(键值对);每一个Entry实际上是一个单向链表,即Entry是键值对链表。
       (02) 当某“弱键”不再被其它对象引用,并被GC回收时。在GC回收该“弱键”时,这个“弱键”也同时会被添加到queue队列中。
        例如,当我们在将“弱键”key添加到WeakHashMap之后;后来将key设为null。这时,便没有外部外部对象再引用该了key。
        接着,当Java虚拟机的GC回收内存时,会回收key的相关内存;同时,将key添加到queue队列中。
       (03) 当下一次我们需要操作WeakHashMap时,会先同步table和queue。table中保存了全部的键值对,而queue中保存被GC回收的“弱键”;
同步它们,就是删除table中被GC回收的“弱键”对应的键值对。

     * 
     */
    //容量表示桶的数量,加载因子表示多满的一种尺度,默认的加载因子是0.75,加载因子过高虽然减少了空间开销,但同时也增加了查询成本
    //哈希表的键值对的数量==容量*加载因子
    //问题:当存有相同哈希值的容量满的时候怎么办?解决办法:addEntry添加2 * table.length
    //createEntry的前提是我们知道要存的数量
    
    //HashMap 是基于“拉链法”实现的散列表。一般用于单线程程序中。
    //HashMap 的实现不是同步的,这意味着它不是线程安全的。它的key、value都可以为null。此外,HashMap中的映射不是有序的。
    //threshold的值="容量*加载因子",当HashMap中存储数据的数量达到threshold时,就需要将HashMap的容量加倍。
    //不是线程安全,不是有序
    //对HashMap的同步处理可以使用Collections类提供的synchronizedMap静态方法,
    //或者直接使用JDK 5.0之后提供的java.util.concurrent包里的ConcurrentHashMap类。
    //HashMap只支持Iterator(迭代器)遍历。
    //HashMap是“从前向后”的遍历数组;再对数组具体某一项对应的链表,从表头开始进行遍历
    //HashMap默认的容量大小是16;增加容量时,每次将容量变为“原始容量x2”。
    //HashMap添加元素时,是使用自定义的哈希算法。即int hash = hash(key.hashCode());
    //Hashtable支持contains(Object value)方法,而且重写了toString()方法;
    static HashMap map=new HashMap();
    
    //Hashtable也是基于“拉链法”实现的散列表。它一般用于多线程程序中。
    //线程安全,不是有序,key,value均不能为null
    //继承于Dictionary,实现了Map、Cloneable、java.io.Serializable接口
    //Hashtable支持Iterator(迭代器)和Enumeration(枚举器)两种方式遍历。
    //Hashtabl是“从后往前”的遍历数组;再对数组具体某一项对应的链表,从表头开始进行遍历。
    //Hashtable默认的容量大小是11;增加容量时,每次将容量变为“原始容量x2 + 1”。
    //Hashtable没有自定义哈希算法,而直接采用的key的hashCode()。即int hash = key.hashCode();
    //HashMap不支持contains(Object value)方法,没有重写toString()方法。
    static Hashtable map2=new Hashtable();
    
    //WeakHashMap也是基于“拉链法”实现的散列表,它一般也用于单线程程序中。
    //WeakHashMap没有实现Cloneable和Serializable接口
    //WeakHashMap的键是“弱引用(WeakReference)
    //对内存的容量来决定使用hashMap还是WeakHashMap
    static WeakHashMap map3=new WeakHashMap();
    
    //TreeMap 是有序的散列表,它是通过红黑树实现的。它一般用于单线程中存储有序的映射。
    //TreeMap继承AbstractMap,实现NavigableMap<K,V>, Cloneable, java.io.Serializable接口,而NavigableMap<K,V>继承SortedMap
    static TreeMap map4=new TreeMap();
    
    public static void main(String[] args) {
        //当我们给hashMap插入相同的key时,覆盖oldValue,并将oldValue返回
        map.put(1, "ffff"+1111);
        Object a1=map2.put(1, "wwww");
        System.out.println(a1);
        map.put(2, 2);
        //get方法通过hashcode的值去查找,然后遍历(顺序查找)链表,直到找到与之相同的key
        System.out.println(map.get(1)+" "+map.containsKey(1)+" "+map.containsValue("wwww"));
        //[1=wwww, 2=2]
        //遍历map,第一步获取entrySet,第二步迭代器
        Iterator itr=map.entrySet().iterator();
        while (itr.hasNext()) {
            Map.Entry entry =(Entry)itr.next();
            System.out.println(entry.getKey()+" "+entry.getValue());
            
        }
        
    }
    

}

4.Collection中的Set

import java.util.HashSet;
import java.util.Iterator;
import java.util.TreeSet;

/*
 * HashSet继承于AbstractSet,并且实现了Set接口。
 * 因为HashSet中只需要用到key,而HashMap是key-value键值对;
 * 
 */

public class SetTest {
    //HashSet 是一个没有重复元素的集合。
    //它是由HashMap实现的,不保证元素的顺序,而且HashSet允许使用 null 元素。
    //非同步
    static HashSet set =new HashSet();
    //TreeSet基于TreeMap的操作
    //TreeSet是非线程安全的,有序的set集合
    static TreeSet set2=new TreeSet();
    
    public static void main(String[] args) {
        set.add("ff");
        set.add("dd");
        set.add("fddf");
        set.add("fsssf");
        set.add("fdddf");
        for(Iterator iterator = set.iterator();
           iterator.hasNext(); ) { 
            System.out.printf("iterator : %s\n", iterator.next());
    }   
        
        
    }
    

}

5.fail-fast的详解

public class fail_fast {
    /*
     * 
     * fast-fail事件产生的条件:当多个线程对Collection进行操作时,若其中某一个线程通过iterator去遍历集合时,
     * 该集合的内容被其他线程所改变;则会抛出ConcurrentModificationException异常。
     * fast-fail解决办法:通过util.concurrent集合包下的相应类去处理,则不会产生fast-fail事件。
     * 
     * 本例中,分别测试ArrayList和CopyOnWriteArrayList这两种情况。ArrayList会产生fast-fail事件,
     * 而CopyOnWriteArrayList不会产生fast-fail事件。 (01)
     * 使用ArrayList时,会产生fast-fail事件,抛出ConcurrentModificationException异常;定义如下:
     * private static List<String> list = new ArrayList<String>(); (02)
     * 使用时CopyOnWriteArrayList,不会产生fast-fail事件;定义如下: private static List<String>
     * list = new CopyOnWriteArrayList<String>();
     * 
     * 快速报错,是指当有其他线程对一个容器(如ArrayList,HashMap)进行了结构性修改,另外一个线程在使用iterator进行迭代,
     * 那么这个迭代线程会抛出并发修改的异常ConcurrentModificationException。
     * 所谓结构性修改,是对原有容器的size造成影响的操作,如remove、add、clear操作等。
     * 
     */
    

    public static void main(String[] args) {
        String string = "a b c c d e";
        //返回一个以' '切割的new ArrayList<T>
        List<String> stringList1 = Arrays.asList(string.split(" "));
        //这个会得到 并发修改异常  ConcurrentModificationException
        //List<String> stringList = new ArrayList<String>(stringList1);
        //COW技术可以消除fail-fast异常,改变运行结果,但是该迭代器不支持remove、set、add
        List<String> stringList = new CopyOnWriteArrayList<String>(stringList1);
       

        /*
         * 可以这样理解,AbstractList实现List接口,实现iterator()的方法,return new Itr();
         * Itr()中让expectedModCount与modCount(ArrayList被修改的次数)相等
         * 在这个例子中只有main线程,也出现了这种情况。
         * 因为,在迭代到”c”时,stringList.remove("c"); 
         * 执行了remove操作,对list造成了结构性修改,改变了list的size,modCount的值会加1。
         * 这样当迭代到”d”时,发现expectedModCount与modCount不等了,因此抛异常ConcurrentModificationException了。
         */
        Iterator<String> iterator = stringList.iterator();
        
        while (iterator.hasNext()) {
            if(iterator.next().equals("c")) {
                //不使用COWArrayList会抛出异常
                stringList.remove("c");
                //java.lang.UnsupportedOperationException
                //iterator.remove();
            }
        }
        System.out.println(stringList);
        
    }
    
}

6.Serializable的详解

/*
 * 所谓序列化其实就是将程序中的数据(对象)通过某种方式,保存到本地中。然后把Java对象转换为字节序列的过程称为对象的序列化;
 * java 的transient关键字为我们提供了便利,你只需要实现Serilizable接口,将不需要序列化的属性前添加关键字transient,
 * 序列化对象的时候,这个属性就不会序列化到指定的目的地中。
 * 一个静态变量不管是否被transient修饰,均不能被序列化。
 * 
 * 若实现的是Externalizable接口,则没有任何东西可以自动序列化,
 * 需要在writeExternal方法中进行手工指定所要序列化的变量,这与是否被transient修饰无关
 */
public class serializable implements Serializable {

    private static final long serialVersionUID = 8294180014912103005L;

    private String username;
    private transient String passwd;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPasswd() {
        return passwd;
    }

    public void setPasswd(String passwd) {
        this.passwd = passwd;
    }

    public static void main(String[] args) {

        serializable user = new serializable();
        user.setUsername("Alexia");
        user.setPasswd("123456");

        System.out.println("read before Serializable: ");
        System.out.println("username: " + user.getUsername());
        System.err.println("password: " + user.getPasswd());

        try {
            ObjectOutputStream os = new ObjectOutputStream(
                    new FileOutputStream("D:/user.txt"));
            os.writeObject(user); // 将User对象写进文件
            os.flush();
            os.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            ObjectInputStream is = new ObjectInputStream(new FileInputStream(
                    "D:/user.txt"));
            user = (serializable) is.readObject(); // 从流中读取User的数据
            is.close();

            System.out.println("\nread after Serializable: ");
            System.out.println("username: " + user.getUsername());
            System.err.println("password: " + user.getPasswd());

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

}

7.总结

系统学习了java集合,感觉多看源码和一些好的博客,学习进步的会更快。

8.参考资料

 

posted @ 2017-09-08 16:54  呼呼呼呼呼65  阅读(209)  评论(0编辑  收藏  举报