java集合类图html
java集合类框架图java
上述类图中,实线边框的是实现类,好比ArrayList,LinkedList,HashMap等,折线边框的是抽象类,好比AbstractCollection,AbstractList,AbstractMap等,而点线边框的是接口,好比Collection,Iterator,List等。算法
发现一个特色,上述全部的集合类,都实现了Iterator接口,这是一个用于遍历集合中元素的接口,主要包含hashNext(),next(),remove()三种方法。它的一个子接口LinkedIterator在它的基础上又添加了三种方法,分别是add(),previous(),hasPrevious()。也就是说若是是先Iterator接口,那么在遍历集合中元素的时候,只能日后遍历,被遍历后的元素不会在遍历到,一般无序集合实现的都是这个接口,好比HashSet,HashMap;而那些元素有序的集合,实现的通常都是LinkedIterator接口,实现这个接口的集合能够双向遍历,既能够经过next()访问下一个元素,又能够经过previous()访问前一个元素,好比ArrayList。数组
还有一个特色就是抽象类的使用。若是要本身实现一个集合类,去实现那些抽象的接口会很是麻烦,工做量很大。这个时候就可使用抽象类,这些抽象类中给咱们提供了许多现成的实现,咱们只须要根据本身的需求重写一些方法或者添加一些方法就能够实现本身须要的集合类,工做流昂大大下降。数据结构
HashSet是Set接口的一个子类,主要的特色是:里面不能存放重复元素,并且采用散列的存储方法,因此没有顺序。这里所说的没有顺序是指:元素插入的顺序与输出的顺序不一致。框架
package edu.sjtu.erplab.collection;import java.util.HashSet;import java.util.Iterator;import java.util.Set;public class HashSetDemo { public static void main(String[] args) { Set<String> set=new HashSet<String>(); set.add("a"); set.add("b"); set.add("c"); set.add("c"); set.add("d"); //使用Iterator输出集合 Iterator<String> iter=set.iterator(); while(iter.hasNext()) { System.out.print(iter.next()+" "); } System.out.println(); //使用For Each输出结合 for(String e:set) { System.out.print(e+" "); } System.out.println(); //使用toString输出集合 System.out.println(set); } }
package edu.sjtu.erplab.collection;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.InputStream;import java.util.HashSet;import java.util.Iterator;import java.util.Scanner;import java.util.Set;public class SetTest { public static void main(String[] args) throws FileNotFoundException { Set<String> words=new HashSet<String>(); //经过输入流代开文献 //方法1:这个方法不须要抛出异常 InputStream inStream=SetTest.class.getResourceAsStream("Alice.txt"); //方法2:这个方法须要抛出异常 //InputStream inStream = new FileInputStream("D:\\Documents\\workspace\\JAVAStudy\\src\\edu\\sjtu\\erplab\\collection\\Alice.txt"); Scanner in=new Scanner(inStream); while(in.hasNext()) { words.add(in.next()); } Iterator<String> iter=words.iterator(); for(int i=0;i<5;i++) { if(iter.hasNext()) System.out.println(iter.next()); } System.out.println(words.size()); } }
ArrayList是List的子类,它和HashSet想法,容许存放重复元素,所以有序。集合中元素被访问的顺序取决于集合的类型。若是对ArrayList进行访问,迭代器将从索引0开始,每迭代一次,索引值加1。然而,若是访问HashSet中的元素,每一个元素将会按照某种随机的次序出现。虽然能够肯定在迭代过程当中可以遍历到集合中的全部元素,但却没法预知元素被访问的次序。函数
package edu.sjtu.erplab.collection;import java.util.ArrayList;import java.util.Iterator;import java.util.List;public class ArrayListDemo { public static void main(String[] args) { List<String> arrList=new ArrayList<String>(); arrList.add("a"); arrList.add("b"); arrList.add("c"); arrList.add("c"); arrList.add("d"); //使用Iterator输出集合 Iterator<String> iter=arrList.iterator(); while(iter.hasNext()) { System.out.print(iter.next()+" "); } System.out.println(); //使用For Each输出结合 for(String e:arrList) { System.out.print(e+" "); } System.out.println(); //使用toString输出集合 System.out.println(arrList); } }
LinkedList是一种能够在任何位置进行高效地插入和删除操做的有序序列。性能
package edu.sjtu.erplab.collection;import java.util.ArrayList;import java.util.Iterator;import java.util.List;import java.util.ListIterator;public class LinkedListTest { public static void main(String[] args) { List<String> a=new ArrayList<String>(); a.add("a"); a.add("b"); a.add("c"); System.out.println(a); List<String> b=new ArrayList<String>(); b.add("d"); b.add("e"); b.add("f"); b.add("g"); System.out.println(b); //ListIterator在Iterator基础上添加了add(),previous()和hasPrevious()方法 ListIterator<String> aIter=a.listIterator(); //普通的Iterator只有三个方法,hasNext(),next()和remove() Iterator<String> bIter=b.iterator(); //b归并入a当中,间隔交叉得插入b中的元素 while(bIter.hasNext()) { if(aIter.hasNext()) aIter.next(); aIter.add(bIter.next()); } System.out.println(a); //在b中每隔两个元素删除一个 bIter=b.iterator(); while(bIter.hasNext()) { bIter.next(); if(bIter.hasNext()) { bIter.next();//remove跟next是成对出现的,remove老是删除前序 bIter.remove(); } } System.out.println(b); //删除a中全部的b中的元素 a.removeAll(b); System.out.println(a); } }
数组的特色是:寻址容易,插入和删除困难;而链表的特色是:寻址困难,插入和删除容易。那么咱们能不能综合二者的特性,作出一种寻址容易,插入删除也容易的数据结构?答案是确定的,这就是咱们要提起的哈希表,哈希表有多种不一样的实现方法,我接下来解释的是最经常使用的一种方法—— 拉链法,咱们能够理解为“链表的数组” ,如图:优化
从上图咱们能够发现哈希表是由数组+链表组成的,一个长度为16的数组中,每一个元素存储的是一个链表的头结点。那么这些元素是按照什么样的规则存储到数组中呢。通常状况是经过hash(key)%len得到,也就是元素的key的哈希值对数组长度取模获得。好比上述哈希表中,12%16=12,28%16=12,108%16=12,140%16=12。因此十二、2八、108以及140都存储在数组下标为12的位置。this
HashMap其实也是一个线性的数组实现的,因此能够理解为其存储数据的容器就是一个线性数组。这可能让咱们很不解,一个线性的数组怎么实现按键值对来存取数据呢?这里HashMap有作一些处理。
1.首先HashMap里面实现一个静态内部类Entry,其重要的属性有 key , value, next,从属性key,value咱们就能很明显的看出来Entry就是HashMap键值对实现的一个基础bean,咱们上面说到HashMap的基础就是一个线性数组,这个数组就是Entry[],Map里面的内容都保存在Entry[]里面。
既然是线性数组,为何能随机存取?这里HashMap用了一个小算法,大体是这样实现:
//存储时:int hash = key.hashCode();// 这个hashCode方法这里不详述,只要理解每一个key的hash是一个固定的int值int index = hash % Entry[].length; Entry[index] = value;//取值时:int hash = key.hashCode();int index = hash % Entry[].length;return Entry[index];
到这里咱们轻松的理解了HashMap经过键值对实现存取的基本原理
3.疑问:若是两个key经过hash%Entry[].length获得的index相同,会不会有覆盖的危险?
这里HashMap里面用到链式数据结构的一个概念。上面咱们提到过Entry类里面有一个next属性,做用是指向下一个Entry。打个比方, 第一个键值对A进来,经过计算其key的hash获得的index=0,记作:Entry[0] = A。一会后又进来一个键值对B,经过计算其index也等于0,如今怎么办?HashMap会这样作:B.next = A,Entry[0] = B,若是又进来C,index也等于0,那么C.next = B,Entry[0] = C;这样咱们发现index=0的地方其实存取了A,B,C三个键值对,他们经过next这个属性连接在一块儿。因此疑问不用担忧。也就是说数组中存储的是最后插入的元素。到这里为止,HashMap的大体实现,咱们应该已经清楚了。
固然HashMap里面也包含一些优化方面的实现,这里也说一下。好比:Entry[]的长度必定后,随着map里面数据的愈来愈长,这样同一个index的链就会很长,会不会影响性能?HashMap里面设置一个因素(也称为因子),随着map的size愈来愈大,Entry[]会以必定的规则加长长度。
开放定址法(线性探测再散列,二次探测再散列,伪随机探测再散列)
再哈希法
链地址法
创建一个公共溢出区
Java中hashmap的解决办法就是采用的链地址法。
package edu.sjtu.erplab.hash;public class Entry<K,V>{ final K key; V value; Entry<K,V> next;//下一个结点 //构造函数 public Entry(K k, V v, Entry<K,V> n) { key = k; value = v; next = n; } public final K getKey() { return key; } public final V getValue() { return value; } public final V setValue(V newValue) { V oldValue = value; value = newValue; return oldValue; } public final boolean equals(Object o) { if (!(o instanceof Entry)) return false; Entry e = (Entry)o; Object k1 = getKey(); Object k2 = e.getKey(); if (k1 == k2 || (k1 != null && k1.equals(k2))) { Object v1 = getValue(); Object v2 = e.getValue(); if (v1 == v2 || (v1 != null && v1.equals(v2))) return true; } return false; } public final int hashCode() { return (key==null ? 0 : key.hashCode()) ^ (value==null ? 0 : value.hashCode()); } public final String toString() { return getKey() + "=" + getValue(); } }
package edu.sjtu.erplab.hash;//保证key与value不为空public class MyHashMap<K, V> { private Entry[] table;//Entry数组表 static final int DEFAULT_INITIAL_CAPACITY = 16;//默认数组长度 private int size; // 构造函数 public MyHashMap() { table = new Entry[DEFAULT_INITIAL_CAPACITY]; size = DEFAULT_INITIAL_CAPACITY; } //获取数组长度 public int getSize() { return size; } // 求index static int indexFor(int h, int length) { return h % (length - 1); } //获取元素 public V get(Object key) { if (key == null) return null; int hash = key.hashCode();// key的哈希值 int index = indexFor(hash, table.length);// 求key在数组中的下标 for (Entry<K, V> e = table[index]; e != null; e = e.next) { Object k = e.key; if (e.key.hashCode() == hash && (k == key || key.equals(k))) return e.value; } return null; } // 添加元素 public V put(K key, V value) { if (key == null) return null; int hash = key.hashCode(); int index = indexFor(hash, table.length); // 若是添加的key已经存在,那么只须要修改value值便可 for (Entry<K, V> e = table[index]; e != null; e = e.next) { Object k = e.key; if (e.key.hashCode() == hash && (k == key || key.equals(k))) { V oldValue = e.value; e.value = value; return oldValue;// 原来的value值 } } // 若是key值不存在,那么须要添加 Entry<K, V> e = table[index];// 获取当前数组中的e table[index] = new Entry<K, V>(key, value, e);// 新建一个Entry,并将其指向原先的e return null; } }
package edu.sjtu.erplab.hash;public class MyHashMapTest { public static void main(String[] args) { MyHashMap<Integer, Integer> map = new MyHashMap<Integer, Integer>(); map.put(1, 90); map.put(2, 95); map.put(17, 85); System.out.println(map.get(1)); System.out.println(map.get(2)); System.out.println(map.get(17)); System.out.println(map.get(null)); } }
package edu.sjtu.erplab.collection;import java.util.WeakHashMap;public class WeekHashMapDemo { public static void main(String[] args) { int size = 100; if (args.length > 0) { size = Integer.parseInt(args[0]); } Key[] keys = new Key[size]; WeakHashMap<Key, Value> whm = new WeakHashMap<Key, Value>(); for (int i = 0; i < size; i++) { Key k = new Key(Integer.toString(i)); Value v = new Value(Integer.toString(i)); if (i % 3 == 0) { keys[i] = k;//强引用 } whm.put(k, v);//全部键值放入WeakHashMap中 } System.out.println(whm); System.out.println(whm.size()); System.gc(); try { // 把处理器的时间让给垃圾回收器进行垃圾回收 Thread.sleep(4000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(whm); System.out.println(whm.size()); } }class Key { String id; public Key(String id) { this.id = id; } public String toString() { return id; } public int hashCode() { return id.hashCode(); } public boolean equals(Object r) { return (r instanceof Key) && id.equals(((Key) r).id); } public void finalize() { System.out.println("Finalizing Key " + id); } }class Value { String id; public Value(String id) { this.id = id; } public String toString() { return id; } public void finalize() { System.out.println("Finalizing Value " + id); } }
输出结果
{50=50, 54=54, 53=53, 52=52, 51=51, 46=46, 47=47, 44=44, 45=45, 48=48, 49=49, 61=61, 60=60, 63=63, 62=62, 65=65, 64=64, 55=55, 56=56, 57=57, 58=58, 59=59, 76=76, 75=75, 74=74, 73=73, 72=72, 71=71, 70=70, 68=68, 69=69, 66=66, 67=67, 85=85, 84=84, 87=87, 86=86, 81=81, 80=80, 83=83, 82=82, 77=77, 78=78, 79=79, 89=89, 88=88, 10=10, 90=90, 91=91, 92=92, 93=93, 94=94, 95=95, 96=96, 97=97, 98=98, 99=99, 20=20, 21=21, 12=12, 11=11, 14=14, 13=13, 16=16, 15=15, 18=18, 17=17, 19=19, 8=8, 9=9, 31=31, 4=4, 32=32, 5=5, 6=6, 30=30, 7=7, 0=0, 1=1, 2=2, 3=3, 29=29, 28=28, 27=27, 26=26, 25=25, 24=24, 23=23, 22=22, 40=40, 41=41, 42=42, 43=43, 38=38, 37=37, 39=39, 34=34, 33=33, 36=36, 35=35} 100 Finalizing Key 98 Finalizing Key 97 Finalizing Key 95 Finalizing Key 94 Finalizing Key 92 Finalizing Key 91 Finalizing Key 89 Finalizing Key 88 Finalizing Key 86 Finalizing Key 85 Finalizing Key 83 Finalizing Key 82 Finalizing Key 80 Finalizing Key 79 Finalizing Key 77 Finalizing Key 76 Finalizing Key 74 Finalizing Key 73 Finalizing Key 71 Finalizing Key 70 Finalizing Key 68 Finalizing Key 67 Finalizing Key 65 Finalizing Key 64 Finalizing Key 62 Finalizing Key 61 Finalizing Key 59 Finalizing Key 58 Finalizing Key 56 Finalizing Key 55 Finalizing Key 53 Finalizing Key 52 Finalizing Key 50 Finalizing Key 49 Finalizing Key 47 Finalizing Key 46 Finalizing Key 44 Finalizing Key 43 Finalizing Key 41 Finalizing Key 40 Finalizing Key 38 Finalizing Key 37 Finalizing Key 35 Finalizing Key 34 Finalizing Key 32 Finalizing Key 31 Finalizing Key 29 Finalizing Key 28 Finalizing Key 26 Finalizing Key 25 Finalizing Key 23 Finalizing Key 22 Finalizing Key 20 Finalizing Key 19 Finalizing Key 17 Finalizing Key 16 Finalizing Key 14 Finalizing Key 13 Finalizing Key 11 Finalizing Key 10 Finalizing Key 8 Finalizing Key 7 Finalizing Key 5 Finalizing Key 4 Finalizing Key 2 Finalizing Key 1 {54=54, 51=51, 45=45, 48=48, 60=60, 63=63, 57=57, 75=75, 72=72, 69=69, 66=66, 84=84, 87=87, 81=81, 78=78, 90=90, 93=93, 96=96, 99=99, 21=21, 12=12, 15=15, 18=18, 9=9, 6=6, 30=30, 0=0, 3=3, 27=27, 24=24, 42=42, 39=39, 33=33, 36=36} 34