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