美文网首页
容器的深入研究

容器的深入研究

作者: zlb | 来源:发表于2016-09-11 23:18 被阅读13次
一.完整的容器分类法
集合框架
二.collection接口

1.boolean add(T)确保容器持有具有泛型类型T的参数,如果没有将此参数添加进容器,则返回false
2.boolean addAll(Collection <? extends T>)添加参数中的所有元素,只要添加了任意元素就返回true
3.void clear() 移除容器中的所有元素
4.boolean contains(T) 如果容器中已经持有泛型类型T此参数,则返回true
5.Boolean containsAll(Collection<?>)如果容器持有此参数中的所有元素,则返回true
6.boolean isEmpty() 容器中没有元素时返回true
7.Iterator<T> iterator() 返回一个Iterator<T> , 可以用来遍历容器中的元素
8.Boolean remove(Object) 如果参数在容器中,则移除此元素的一个实例,如果做了一处操作,则返回true
9.boolean removeAll(Collection<?>) 移除参数中的所有元素,只要有移除动作就返回true
11.Boolean retainAll(Collection<?>) 只保存参数中的元素,只要Collection发生了改变就返回true
12.int size() 返回容器中元素的数目
13.Object[] toArray() 返回一个数组,该数组包含容器中的所有元素
14.<T> T[] toArray(T[] a)返回一个数组该数组包含容器中的所有元素,返回结果的运行时类型参数与参数数组a的类型相同,而不是单纯的Object

三.UnsupportedOperationException(未获支持的操作) 异常

List list = Arrays.asList(array) 当对list 进行增删操作时 就会抛出UnsupportedOperationException异常
对于UnsupportedOperationException 异常:
1.它必须是一个罕见的事件
2.如果一个操作时未获支持的,那么在实现接口的时候可以抛出这个异常
3.它只能在运行时才能被检测到

四.List功能方法
五.Set和存储顺序
集合名称 集合说明
Set(interface) 存入Set的每个元素必须是惟一的,因为Set不保存重复的元素,加入Set的元素必须定义equals()方法一确保对象的唯一性。Set接口不保证维护元素的次序
HashSet 为快速查找而设计的Set,存入的HashSet的元素必须定义hashCode(),保持次序的Set,底层为树结构。使用它可以从Set中提取有序的序列,元素必须实现Comparable接口
LinkedHashSet 具有HashSet的查询速度,且内部使用链表维护元素的顺序,于是在使用迭代遍历Set时,结果会按元素插入的次序显示。元素必须定义hashCode()方法
public class SetType {
    int i;
    public SetType(int n){
        this.i = n;
    }
    public boolean equals(Object obj){
        return obj instanceof SetType && (i == ((SetType)obj).i);
    }
    public String toString(){
        return Integer.toString(i);
    }
}


 public class HashType extends SetType{
    public HashType(int n){ super(n);}
    public int hashCode(){return i;}
}

 public class TreeType extends SetType implements Comparable<TreeType>{
    public TreeType(int n) {
        super(n);
    }
    @Override
    public int compareTo(TreeType type) { 
        return (type.i < i ? -1 : (type.i == i ? 0 : 1));
    }
}
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.TreeSet;
public class TypesForSet {
    static <T>Set<T> fill(Set<T> set,Class<T> type){
        try{
            for(int i=0;i<10;i++){
        set.add(type.getConstructor(int.class).newInstance(i));
            }
        }catch(Exception ex){
            throw new RuntimeException();
        }
        return set;
    }
    static <T> void test(Set<T> set,Class<T> type){
        fill(set, type);
        fill(set, type);
        fill(set, type);
        System.out.println(set);
    }
    public static void main(String[] args) {
        test(new HashSet<HashType>(), HashType.class);
        test(new LinkedHashSet<HashType>(), HashType.class);
        test(new TreeSet<TreeType>(), TreeType.class);
        test(new HashSet<SetType>(), SetType.class);
        test(new HashSet<TreeType>(), TreeType.class);
        test(new LinkedHashSet<SetType>(), SetType.class);
        test(new LinkedHashSet<TreeType>(), TreeType.class);
    }
}

从输出可以看出,HashSet以某种神秘的顺序白痴所有的元素,LinkedHashSet按照元素的插入顺序保存元素,而TreeSet按照排序顺序维护顺序(按照CompareTo()的实现形式,这里维护的 降序)

  • SortedSet:
    1.Comparator comparator()返回当前set使用的Comparator,或者返回null,表示自然方式排序
    2.Object first()返回容器总的第一个元素
    3.Object last()返回容器中的最末一个元素
    4.SortedSet subSet(fromElement, toElement)生成此Set的自己,范围从fromElement(包含)到toElement(不包含)
    5.SortedSet headSet(toElement)生成此Set的子集,由小于toElement的元素组成
    6.SortedSet tailSet(fromElement)生成此Set的子集,由大于或等于fromElement的元素组成
六.队列
  • Queue在java se5中只有两个实现,LinkedList和PriorityQueue,他们的差异在排序行为
    PriorityQueue的排序也是通过Comparable而进行控制
  • 双向队列:
    就像是一个队列,但是可以在任何一段添加或移除元素,在LinkedList中包含支持双向队列的方法,但是在java标准类库中没有任何显式地用于双向队列的接口。因此可以使用组合来创建一个Deque类,并直接从LinkedList中暴露
七.Map
  • 实现一个简单的Map
  package com.zlb.map;
public class AssociativeArray<K,V>{
    private Object pairs[][];
    private int index;
    public AssociativeArray(int length){
        pairs = new Object[length][2];
    }
    public void put(K key,V value){
        if(index >= pairs.length)
            throw new ArrayIndexOutOfBoundsException();
        pairs[index++] = new Object[]{key,value};
    }
    @SuppressWarnings("unchecked")
    public V get(K key){
        for(int i=0;i<index;i++){
            if(key.equals(pairs[i][0])){
                return (V)pairs[i][1];
            }
        }
        return null;
    }
    public String toString(){
        StringBuffer stu = new StringBuffer();
        for(int i=0;i<index;i++){
            stu.append(pairs[i][0].toString());
            stu.append(" : ");
            stu.append(pairs[i][1].toString());
            if(i < index - 1)
                stu.append("\n");
        }
        return stu.toString();
    }
    public static void main(String[] args) {
        AssociativeArray<String,String> map = new       AssociativeArray<String,String>(6);  
        map.put("name", "zhoulibin");
        map.put("age", "22");
        map.put("sex", "man");
        System.out.println(map);
        System.out.println(map.get("name"));
    }
}
集合名称 集合说明
HashMap* Map基于散列表的实现(他取代了HashTable),插入和查询"键值对"的开销是固定的。可以通过构造器设置容量和负载因子,已调整容器的性能
LinkedHashMap 类似于HashMap,但是迭代遍历它时,取得"键值对"的顺序是器插入次序,或者是最近少使用(LRU)的次序。只比HashMap慢点;而在迭代访问时更快,因为它使用链表维护内部次序
TreeMap 基于红黑树的实现。查看"键"或"键值对"时,他们会被排序(次序由Comparable或Comparator决定)。TreeMap的特点在于,所得到的结果是经过排序的。TreeMap是唯一的带有subMap()方法的Map
WeakHashMap 弱键映射,允许释放映射所指向的对象
CurrentHashMap 一种线程安全的Map,它不涉及同步加锁,并发用的较多
IdentityHashMap 使用==代替equals()对"键"进行比较的散列映射

对Map中使用的键的要求与对Set中的元素的要求一样,任务键都必须具有一个equals()方法,如果键被用于散列Map,那么他必须还具有恰当的hashCode()方法,如果键被用于TreeMap,那么他必须实现Comparable

八.散列与散列码
 package com.zlb.hashcode;
public class Groundhog {
    protected int number;
    public Groundhog(int n){number = n;}
    public String toString(){
        return "Groundhod # "+ number;
    }
}

package com.zlb.hashcode;
import java.util.Random;
public class Prediction {
    private static Random random = new Random(47);
    private boolean shadow = random.nextDouble() > 0.5;
    public String toString(){
        if(shadow)
            return "Six more weeks";
        else 
            return "Spring";
    }
}
 package com.zlb.hashcode;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
public class SpringDector {
    public static <T extends Groundhog> void detectSpring(Class<T> type) throws Exception{
        Constructor<T> ghog = type.getConstructor(int.class);
        Map<Groundhog,Prediction> map = new HashMap<Groundhog,Prediction>();
        for(int i=0;i<10;i++){
            map.put(ghog.newInstance(i), new Prediction());
        }
        System.out.println("map:"+map);
        Groundhog gh = ghog.newInstance(3);
        System.out.println("looke up f or:"+gh);
        if(map.containsKey(gh)){
            System.out.println(map.get(gh));
        }else{
            System.out.println("key not found:" + gh);
        }
    }
    public static void main(String[] args) throws Exception {
        detectSpring(Groundhog.class);
    }
}

从输出的结果看,数字3的键无法找到,因为Groundhog自动继承Object类,使用了Object累的hashCode()方法生成了散列码,而它默认使用对象的地址计算散列码,因此,由Groundhog(3)生成的第一个实例的散列码与由Groundhog(3)生成的散列码是不同的,所以找不到
HashMap使用equals()判断当前的键是否与表中存在的键相同
正确的equals()方法必须满足下列5个条件:
1.自反性。对任意x,x.equals(x)一定返回true
2.对称性。对任意x和y,如果y.equals(x)返回true,则x.equals(y)也返回true
3.传递性。对任意的x,y.z 如果有x.equals(y)返回true,y.equals(z)返回true,则x.equals(z)一定返回true
4.一致性。
5.对任何不是null的x,x.equals(null)一定返回false

  • 理解hashCode()
    hashCode()特点:
    1.无论何时,对同一个对象调用hashCode()都应该生成同样的值。
    2.hashCode()不能依赖于具有唯一性的对象信息,应该使用对象内有意义的识别信息。
    3.对于String类,hashCode()是基于String的内容的;
    4.散列码不必是独一无二的,应该更关注速度,而不是唯一性,通过hashCode()和equals(),必须能够完全确定对象的身份。
    5.好的hashCode()应该产生分布均匀的散列码。

  • 为速度而散列
    1.散列的价值在于速度:散列使得查询得以快速进行。由于瓶颈位于键的查询速度,因此解决方案之一就是保持键的排序状态。
    2.存储一组元素最快的数据结构是数组,因此用数组保存键的信息。数组并不保证键的本身,而是通过键对象生成一个数字,将其作为数组的下标。这个数字就是散列码
    3.查询一个值的过程就是首先计算散列码,然后使用散列码查询数组,如果能保证没有冲突,那可就有了一个完美的散列函数,但是这种情况只是特例。通常情况下,冲突由**外部链接
    **处理:数组并不保存值,而是保存值的list。然后对list中的值使用equals()方法进行线性查询。这部分的查询会比较慢,如果散列函数好的话,数组的每个位置就只有很少的值。因此,不是查询整个list,而是快速跳转到数组的某个位置,对很少的元素进行比较,这就是HashMap会如此快的原因

以下实现一个简单的HashMap:

 package com.zlb.hashcode;
import java.util.AbstractMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
public class SimpleHashMap<K, V> extends AbstractMap<K, V> {
    static final int SIZE = 997;
    @SuppressWarnings("unchecked")
    LinkedList<MapEntry<K, V>>[] buckets = new LinkedList[SIZE];
    @Override
    public Set<Map.Entry<K, V>> entrySet() {
        Set<Map.Entry<K, V>> set = new HashSet<Map.Entry<K, V>>();
        for (LinkedList<MapEntry<K, V>> bucket : buckets) {
            if (bucket == null) {
                continue;
            }
            for (MapEntry<K, V> mpair : bucket) {
                set.add(mpair);
            }
        }
        return set;
    }
    public V put(K key, V value) {
        V oldValue = null;
        int index = Math.abs(key.hashCode()) % SIZE;
        if (buckets[index] == null) {
            buckets[index] = new LinkedList<MapEntry<K, V>>();
        }
        LinkedList<MapEntry<K, V>> bucket = buckets[index];
        MapEntry<K, V> pair = new MapEntry<K, V>(key, value);
        boolean found = false;
        ListIterator<MapEntry<K, V>> it = bucket.listIterator();
        while (it.hasNext()) {
            MapEntry<K, V> iPair = it.next();
            if (iPair.getKey().equals(key)) {
                oldValue = iPair.getValue();
                it.set(pair);
                found = true;
                break;
            }
        }
        if (!found) {
            buckets[index].add(pair);
        }
        return oldValue;
    }
    public V get(Object key) {
        int index = Math.abs(key.hashCode()) % SIZE;
        if (buckets[index] == null)
            return null;
        for (MapEntry<K, V> iPair : buckets[index])
            if (iPair.getKey().equals(key))
                return iPair.getValue();
        return null;
    }
    public static void main(String[] args) {
        SimpleHashMap<String, String> m = new SimpleHashMap<String, String>();
        m.put("firstName", "zhou");
        m.put("lastName", "libin");
        System.out.println(m);
        System.out.println(m.get("lastName"));
        System.out.println(m.entrySet());
    }
}

相关文章

网友评论

      本文标题:容器的深入研究

      本文链接:https://www.haomeiwen.com/subject/moiuettx.html