Java--集合

来源:互联网 发布:我知主掌握明天歌谱 编辑:程序博客网 时间:2024/05/16 10:15

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.参考资料

Java 集合系列目录(Category)
关于快速报错fail-fast想说的之fail-fast的实现原理
Java transient关键字使用小记

 

原创粉丝点击