zoukankan      html  css  js  c++  java
  • 关于集合框架的基本的介绍(JDK7)

    集合框架中的接口以及继承关系

      关于集合框架中的接口,注意下面列出的全都是接口:

    clip_image001[7]


      接口与具体的实现类蓝色表示接口橙色表示具体实现类(仅仅列出比较常用的一些):

    clip_image001[9]

      这个图是一个具体的集合框架的接口以及实现类的图,可以和上面的全部接口相对应:

    clip_image005

      因为collection有很多的子类,为了操作方便,并没有哪个类直接实现了collecion接口,而是直接对collection接口进行继承,仅仅是提供了更加具体的子接口。

    关于List

    ArrayList的基本使用:
    package com.javase.collection;
    
    import java.util.ArrayList;
    import java.util.LinkedList;
    
    public class testList {
        public static void main(String[] args) {
            //最好是通过泛型的方式来使用 负责取出元素的时候还需要进行强制类型转化
            ArrayList<String> list=new ArrayList<String>();
            //通过append的方式进行追加
            list.add("abc");
            list.add("def");
            list.add("ghi");
            list.add("jkl");
            //获得固定下标的元素
            String a=list.get(0);
            int s=list.size();
            System.out.println("the size is "+s);
            for(int i=0;i<s;i++)
            {
                System.out.println(list.get(i));
            }
            //判断某个元素的索引
            System.out.println("the index of abc is "+ list.indexOf("def"));
            //删除固定索引的元素
            list.remove(0);
            System.out.println("after the remove option the first element is "+ list.get(0));
    
            //转化为一个数组 
            //这里要是写成 String [] str=(String [])list.toArray();在执行的时候就会抛出异常
            //只能在使用的时候 先取出一个个 Object对象 再进行强制转化 
            //因为仅仅是String Integer等等这些类型继承了Object 而String[] Integer[]并没有继承Object[]
            //因此这里只能使用Object[]来进行强制类型转化 使用别的就会报错
            Object [] str=(Object [])list.toArray();
            System.out.println("--------------------");
            for(int i=0;i<str.length;i++)
            {
                System.out.println(str[i]);
            }
            LinkedList linklist=new LinkedList();
            //直接可以将其他的集合类型作为参数传入 生成一个ArrayList
            ArrayList list3=new ArrayList(linklist);
            //clear清除全部内容
            list.clear();
            System.out.println("after clear option the size is "+list.size());
    
        }
    
    }
    
    
    输出结果:
    the size is 4
    abc
    def
    ghi
    jkl
    the index of abc is 1
    after the remove option the first element is def
    --------------------
    def
    ghi
    jkl
    after clear option the size is 0

    关于ArrayList源码的简要分析:

      类的声明的结构如下

      public class ArrayList<E> extends AbstractList<E>

      implements List<E>, RandomAccess, Cloneable, java.io.Serializable

      注意看整个ArrayList类声明的时候就是通过泛型的方式来进行的

      一共有三个构造方法:

      第一个是通过声明一个capacity

      可以看到在构造函数中 关键的部分是:

      this.elementData = new Object[initialCapacity];

      其中elementData是个Object类型的数组 初始的时候,实际ArrayList的底层是通过这个Object类型的数组来维护的。

      第二个是没有参数的形式

    public ArrayList() {

    this(10);

    }

      可以看到这段,要是不指定capacity的话,ArrayList底层默认生成一个capacity为10的数组。

      第三个是通过传入另外一个集合框架 将其中的内容转化成为一个ArrayList

      比如可以生成一个LinkedList 对象 将其作为形参传入 生成ArrayList

    关于add方法

      注意添加新元素之前要对集合的siz+1进行检测,保证当前底层的Object数组的空间是足够的,这一步通过ensureCapacityInternal(size + 1);这个函数来进行,够的话就直接放入,size增加1,要是不够的话会通过调整newCapacity来继续进行,调整之后将原来的元素拷贝到一个新的Object数组中,数组空间大小为newCapacity(通过Arrays.copyof来进行):

      elementData = Arrays.copyOf(elementData, newCapacity);

      还可以添加参数,将元素插入到指定的位置,但这种方式需要将插入位置后面的部分都进行移动,代价比较高通过这个函数来进行:

      System.arraycopy(elementData, index, elementData, index + 1,

    size - index);

      函数原型声明:(void java.lang.System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length))

    关于remove方法

      这个其实与上面在固定位置插入元素的操作很相似,只不过拷贝的方式是向前进行的。

      由于ArrayList的底层就是一个Object数组,因此在固定位置上的插入和删除操作代价都是比较高的。

    关于LinkedList

      这个也算是数据结构中最基本的了,就是链表的结构,根据链表自身的结构特性,比起ArrayList又有一些特殊的方法,比如头插addFirst 尾插 addLast。

      当然LinkedList底层肯定维护的是一个链表,不想ArrayList那样维护一个数组,这个是结点Node 的部分,是一个用泛型来实现的静态内部类,内容就是我们通常意义上的具体元素值,前驱指针,后继指针:

    private static class Node<E> {

    E item;

    Node<E> next;

    Node<E> prev;

    Node(Node<E> prev, E element, Node<E> next) {

    this.item = element;

    this.next = next;

    this.prev = prev;

    }

    }

      具体的插入删除操作都与通常数据结构书中介绍的内容类似。

      还是那些老生常谈的问题,再注意一下,也就是线性表和链表的区别,插入删除操作的时候用LinkedList比较快,指定位置检索元素的时候用ArrayList比较快。

    关于Set的机制

    Hashcode与equals方法的补充说明

      首先要再对hashcode 以及equal方法再进一步进行说明,之前的一些说明可以参(http://www.cnblogs.com/Goden/p/3990985.html)。

    进一步补充:

      hashcode的一般要求:

      1、 一致性,同一个对象的hashcode方法被多次调用之后应当返回一样的值(前提是该对象的内容没有发生变化)

        2、 两个对象a,b,如果a.equals(b)返回的是true,则对象a和b的hashcode值要是相同的。

      3、 两个对象 a,b 如果a.equals(b)返回的是false,则对象 a 和 b 的hashcode不要求一定相同,即使说在hashcode相同的时候,a.equals(b)不一定是true,也可以是false。

      对于Object类,其hashcode表示的是对象的地址,因此不同Object实例,其对象是不同的。

      一般一个自定义的类的equal方法重写之后,对应的hashcode方法也需要进行重写,这是为了保证通过equal比较相等的对象有相同的hashcode。

      当使用HashSet的时候,若是增加一个元素,则会判断已经存在集合中的所有元素的hashcode的值是否与增加的这个元素的hashcode的值是一致的。如果不一致,则将这个元素加进去。如果与某个对象一致(hashcode一样),按照前面的描述,这个时候equals方法还有可能返回的是false于是再进行equals方法的调用,若和这个对象进行比较,若equals返回为true,说明这个元素已经加入集合,就不再进行添加了。若equals返回为 false,说明这个元素还没有加入到集合中(就是那种hashcode相同而equals不相同的情况)Hashcode相同而equals不同的例子可能不太好想到,比如下面这个:

    package com.testcollection;
    
    import java.util.HashSet;
    
    class People{
        String name;
        public People(String name){
            this.name=name;
        }
    }
    
    public class testHashcode {
        public static void main(String[] args) {
            HashSet<People> set=new HashSet<People>();
            //这两次添加就成功了 因为按照object类的hashcode和equal方法 这个是两个不同的
            //但实际上里面的内容是相同的
            set.add(new People("abc"));
            set.add(new People("abc"));
            //像这种情况 虽然两个类的hashcode并不相同 根据打印结果就能看出来 默认的toString方法 在@后面 就是hashcode值 但是实际上他们的内容是一样的
            //属性的情况比较复杂的话 就不能满足需求了
            //这种情况下 Object类中的hashcode与equals方法就不能满足实际的需要了 需要重写
            System.out.println(set);
            
            People p1=new People("def");
            System.out.println(set.add(p1));
            //这个添加就失败了
    //这里两次添加的都是p1指针指向的是同一个对象 不像是上面那种情况 指向的是不同的两个对象(虽然对象的内容是相同的 但是是生成了两次的)
    System.out.println(set.add(p1)); } } /* 打印结果 [com.testcollection.People@55fe910c, com.testcollection.People@3be4d6ef] true false */

      实际情况下,应当对Person中的hashcode和equals方法进行重写,hashcode就直接用属性的hashcode,而equals方法则参照Object类中的equals方法来实现具体可以参考(http://www.cnblogs.com/Goden/p/3990985.html

      下面就是重写的例子:

    package com.test.collection;
    
    import java.util.HashSet;
    
    public class testHashcode {
    
        public static void main(String[] args) {
            HashSet<People> set=new HashSet<People>();
            System.out.println(set.add(new People("abc")));
            //由于重写了people类的hashcode 以及equals方法 第二次插入就会返回false 插入失败
            System.out.println(set.add(new People("abc")));
    
        }
    }
    
    class People {
        String name;
    
        public People(String name) {
            this.name = name;
        }
    
        public int hashCode() {
            return this.name.hashCode();
        }
    
        public boolean equals(Object obj) {
            boolean b = false;
    
            if (this == obj) {
                b = true;
            }
            if (null != obj && obj instanceof People) {
                People p = (People) obj;
    
                if (name.equals(p.name)) {
                    b = true;
                } else {
                    b = false;
                }
            }
    
            return b;
        }
    }

      实际开发中对于hashcode以及equals方法的重写通常是需要通过一一比较类中的内容,这样进行下去。由于实际情况中这个用的还是比较普遍,通常直接在编译器的source->generate equals and hashcode的操作这样就能继续往下进行。

    迭代器

      各个集合的实现都有一个iterator()方法,这个方法主要是用来返回一个迭代器。

      迭代器的最顶层就是一个用泛型来实现的public interface Iterator<E>接口,方法也比较简单,主要有三个:hasNext Next 以及remove。

      hasNext表示集合中是否还有更多的元素,如果集合中还有更多元素的话,这个结果就返回true,要是没有更多元素的话,就返回false

    Next 表示返回下一个元素,注意每次调用了Next之后,要执行两个操作:返回下一个元素,以及往后跳动一个位置。remove 这个是用于安全地删除集合中的元素,具体的集合的实现类一般都对这个方法进行了不同的实现。

    通常用到的是前两个方法。

    下面是一个简要的逻辑图,便于理解next以及hasnext,注意初始的时候:

    clip_image001[11]

      由于每个集合框架都对这个Iterator接口有具体的实现,实际中也要是通过iterator函数返回一个iterator迭代器,之后通过这个迭代器来遍历集合当中的每个元素,这样一次可以返回一个元素,通常使用一个循环来遍历集合中的每个元素,格式比较固定,主要是三步:调用iterator方法得到迭代函数,建立hasNext()循环迭代,只要返回值为true就继续进行,在循环内部通过next方法得到下一个的元素(指针会同时后移)。

      下面是一个基本的例子,对应的写法要掌握:

    package com.test.collection;
    
    import java.util.HashSet;
    import java.util.Iterator;
    
    public class testIterator {
        public static void main(String[] args) {
            HashSet<Integer> set=new HashSet<Integer>();
            for(int i=0;i<=10;i++){
                set.add(i);
            }
            //通过迭代的方式遍历集合 打印出集合中全部元素
            for(Iterator<Integer> iter=set.iterator();iter.hasNext();){
                System.out.println(iter.next());
            }
        }
    }
    关于HashSet与TreeSort

      通过名称也比较好理解,HashSet就是最直接的Set的机制,最后输出的话,输出元素的顺序是凌乱的而且每次输出的时候都不一样,TreeSet就是集合+排序,里面的元素在每次添加的时候都是自动排好序的,TreeSet实现了SortedSet的接口,里面元素的顺序是有序的,输出的时候就是按序输出的。注意定义TreeSet的时候,如果里面的元素是非原始的数据类型,一定要传入一个自定义的比较器,要不然就没法往TreeSet中放元素,一定要先通过TreeSet来指定好元素的比较顺序。

    package com.test.collection;
    
    import java.util.HashSet;
    import java.util.Iterator;
    import java.util.TreeSet;
    
    public class testIterator {
        public static void main(String[] args) {
            HashSet<Integer> set=new HashSet<Integer>();
            TreeSet<Integer> treeset=new TreeSet<Integer>();
            for(int i=100;i>=1;i--){
                set.add(i);
                treeset.add(i);
            }
            //通过迭代的方式遍历集合 打印出集合中全部元素 这个输出就是无序的
            for(Iterator<Integer> iter=set.iterator();iter.hasNext();){
                System.out.println(iter.next());        
            }
            //放到TreeSet里面的输出就是有序的 默认是从小到大的
            for(Iterator<Integer> siter=treeset.iterator();siter.hasNext();){
                System.out.println(siter.next());        
            }
        }
    }
    关于Comparator接口

      由于TreeSet实现了SortedSet接口,每次新加入进来的元素都是有序存放的,这里就涉及到了排序的问题。

      就像C++的sort的实现一样,如果是自定义的排序问题的话,特别是对于结构话的数据进行比较,需要引入一个Comparator接口,在声明集合的时候将比较器传入。这个接口中有一个compare方法,自定义的比较器实现了这个接口之后再传入TreeSort中就可以按照自定义的模式进行比较了。

      注意compare方法的返回类型是int类型,按照自定义的比较方式,如果:

      第一个参数小于第二个参数 返回一个负数

      第一个参数等于第二个参数 返回0

      第一个参数大于第二个参数 返回一个正数

      下面是一个基本的在定义TreeSet集合的时候使用自定义Comparator构造器的例子:

    package com.test.tempTest;
    
    import java.util.Comparator;
    import java.util.Iterator;
    import java.util.TreeSet;
    
    class Person {
        int id;
        String name;
    
        public Person(int id, String name) {
            super();
            this.id = id;
            this.name = name;
        }
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + id;
            result = prime * result + ((name == null) ? 0 : name.hashCode());
            return result;
        }
    
        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            Person other = (Person) obj;
            if (id != other.id)
                return false;
            if (name == null) {
                if (other.name != null)
                    return false;
            } else if (!name.equals(other.name))
                return false;
            return true;
        }
    }
    
    class Mycompare implements Comparator<Person> {
    
        // sorted by name increasing
        public int compare(Person p1, Person p2) {
    
            int result = p1.getName().compareTo(p2.getName());
            if (result == 0) {
                result = p1.getId() - p2.getId();
            }
            return result;
        }
    
    }
    
    public class testbasicComparator {
        public static void main(String[] args) {
            // 声明的时候可以传入一个实现了comparator接口的类作为自定义比较器
            TreeSet<Person> treeset = new TreeSet<Person>(new Mycompare());
            treeset.add(new Person(1, "dfe"));
            treeset.add(new Person(2, "efd"));
            treeset.add(new Person(3, "abc"));
            treeset.add(new Person(4, "dfe"));
            // 通过迭代器输出
            for (Iterator<Person> iter = treeset.iterator(); iter.hasNext();) {
                Person p = iter.next();
                System.out.println("the name is: " + p.getName() + " the id is "
                        + p.getId());
            }
        }
    }
    
    /*
    输出结果如下,可以看出来结果符合Comparator中定义的顺序:
    the name is: abc the id is 3
    the name is: dfe the id is 1
    the name is: dfe the id is 4
    the name is: efd the id is 2
    */
    关于Collections类

      Arrays类是数组方法的工具类,里面提供的许多对于数组操作的静态方法。类似地Collections是一个集合的工具类,里面提供了许多对于集合的操作的静态方法,操纵的目标是集合对象,为集合对象提供辅助的方法,比如最基本的sort方法可以提供实现了List接口的集合的排序。

        还有一些比较常用到的辅助函数:
        比如Collections.shuffle(List<E>list)这个主要功能是将list中元素的顺序弄乱,在快速排序的时候,用这个可以避免最差的那种情况,来提升效率。

      还有就是Collections.min(List<E>list)以及Collections.max(List<E>list)用于返回序列中的最大和最小的元素。

        整个Comparator就是基于策略模式实现的,下面是一个基于策略模式实现的比较的例子:一个Person类中有id,name,age三个属性,通过泛型来自定义一个比较器,通过策略模式来传入一种sort策略,进行排序。

    //首先是一个基本的Person类
    package com.test.collector;
    
    public class Person {
           int id;
           int age;
           String name;
        
        public Person(int id, int age, String name) {
            super();
            this.id = id;
            this.age = age;
            this.name = name;
        }
        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        
        //注意重写hashcode以及equals方法
        //这里可以通过eclispe来自动生成
        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + age;
            result = prime * result + id;
            result = prime * result + ((name == null) ? 0 : name.hashCode());
            return result;
        }
        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            Person other = (Person) obj;
            if (age != other.age)
                return false;
            if (id != other.id)
                return false;
            if (name == null) {
                if (other.name != null)
                    return false;
            } else if (!name.equals(other.name))
                return false;
            return true;
        }
        
           
    }
    
    //Sort接口:
    package com.test.collector;
    
    import java.util.List;
    
    public interface Sort {
        public void sort(List<Person> list);
    }
    
    //Sort接口的两种实现,同时实现了Comparator接口:
    package com.test.collector;
    
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.List;
    
    //这里同时还要实现comparator接口
    public class upNameSort implements Sort,Comparator<Person> {
    
        public void sort(List<Person> list) {
            //通过collections工具类的sort方法来进行排序
            Collections.sort(list,this);
    
        }
    
    
        public int compare(Person p1, Person p2) {
            int result=p1.getName().compareTo(p2.getName());
            //根据id来进行
            if(result==0){
                result=p1.getId()-p2.getId();
            }
            return result;
        }
    
    }
    
    package com.test.collector;
    
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.List;
    
    public class upIdSort implements Sort,Comparator<Person> {
    
    
        public void sort(List<Person> list) {
            Collections.sort(list, this);
            
        }
    
    
        public int compare(Person p1, Person p2) {
            int result=p1.getId()-p2.getId();
            return result;
        }
    
    }
    
    //通过这个类来体现策略模式 通过传入的way参数来调用不同的方式进行排序:
    package com.test.collector;
    
    import java.util.List;
    
    public class SortWay {
        private Sort dosort;
        private int way;
    
        public Sort getDosort() {
            return dosort;
        }
    
        public void setDosort(Sort dosort) {
            this.dosort = dosort;
        }
    
        public void doSort(List<Person> list, int way) {
            if (way == 1) {
                this.dosort = new upIdSort();
                dosort.sort(list);
            }
            if (way == 2) {
                this.dosort = new upNameSort();
                dosort.sort(list);
            }
    
        }
    
    }
    
    //Client类 用于进行实际的测试:
    package com.test.collector;
    
    import java.util.ArrayList;
    import java.util.Iterator;
    
    public class Client {
        public static void main(String[] args) {
            ArrayList<Person> list = new ArrayList<Person>();
            list.add(new Person(4, 23, "abc"));
            list.add(new Person(2, 24, "cdf"));
            list.add(new Person(3, 25, "fgr"));
            list.add(new Person(1, 26, "abc"));
    
            SortWay sw = new SortWay();
            // 此时是按照id排序
            sw.doSort(list, 1);
    
            for (Iterator<Person> iter = list.iterator(); iter.hasNext();) {
                Person p = iter.next();
                System.out.println("the name is :" + p.getName() + " the id is :"
                        + p.getId());
            }
    
            System.out.println("--------------------------");
            sw = new SortWay();
            // 此时是按照name排序
            sw.doSort(list, 2);
    
            for (Iterator<Person> iter = list.iterator(); iter.hasNext();) {
                Person p = iter.next();
                System.out.println("the name is :" + p.getName() + " the id is :"
                        + p.getId());
            }
        }
    }
    
    /*
    输出结果:
    the name is :abc the id is :1
    the name is :cdf the id is :2
    the name is :fgr the id is :3
    the name is :abc the id is :4
    --------------------------
    the name is :abc the id is :1
    the name is :abc the id is :4
    the name is :cdf the id is :2
    the name is :fgr the id is :3
    
    上面是按照id进行排序 下面是按照name字典顺序进行的排序
    */

    关于Map的机制

    关于map的基本概念

        Map<K,V>数据结构主要用于表示一个key<—>value的键值对,key与value可以是通过泛型来定义的不同的数据类型,他们之间的的关系是一一对应的,一个key映射到一个value值上,但是所有的Key值是不可以重复的,如果add相同的key值,但是value不同的键值对进入map,貌似后插入的一个key-value值会将先前的key-value值覆盖掉,最后只存最后的key-value值。

        通常使用的就是HashMap,就像HashSet一样,存入进来的键值对再取出的时候是乱序的。

        放入的时候直接map.put(“key”,”value”)取value值的时候通过key值来作为索引取出,比如取出一个String类型的value,String value=map.get(“key”)。(注意在map中是使用put而不是使用add)

        关于迭代输出的时候通常有两种方法:

        1、 通过返回map中所有key的集合(set类型),再依次通过map.get(“key”)取出所有的value的值。

        2、 通过返回一个entry结构的集合(Map中的一个内部类,这个类中维护了key value的信息)具体见下面的源码分析

        下面是一个Map基本操作的例子:

    package com.test.testMap;
    
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Map.Entry;
    import java.util.Set;
    
    public class basicMap {
        public static void main(String[] args) {
            HashMap<String, String> map = new HashMap<String, String>();
            map.put("a", "aa");
            map.put("b", "bb");
            map.put("c", "cc");
            map.put("d", "aa");
            // 加入了key相同的键值对 后加入的一个先加入的一个键值对替代掉
            map.put("a", "bb");
    
            // 通过key的集合来迭代
            Set<String> set = map.keySet();
            for (Iterator<String> iter = set.iterator(); iter.hasNext();) {
                String key = iter.next();
                String value = map.get(key);
                System.out
                        .println("the key is :" + key + " the value is :" + value);
            }
    
            System.out.println("--------------------");
            // 通过第二种方式进行迭代 通过返回一个Entry的集合来进行
            Set<Entry<String, String>> entryset = map.entrySet();
            for (Iterator<Entry<String, String>> entryiter = entryset.iterator(); entryiter
                    .hasNext();) {
                Entry<String, String> e = entryiter.next();
                String key = e.getKey();
                String value = e.getValue();
                System.out
                        .println("the key is :" + key + " the value is :" + value);
            }
        }
    }
    /*
    输出的结果如下,可以看出来后加进来的关键字相同的(a,bb)键值对代替了最开始输入的的(a,aa)键值对。
    
    
    the key is :d the value is :aa
    the key is :b the value is :bb
    the key is :c the value is :cc
    the key is :a the value is :bb
    ------------------------------
    the key is :d the value is :aa
    the key is :b the value is :bb
    the key is :c the value is :cc
    the key is :a the value is :bb
    */
    关于HashSet以及HashMap的简要源码分析

        HashSet的底层是由HashMap来实现的,只是从逻辑上来看的话,就抽象成了一个HashSet,事实上在HashSet的源码中:

        属性值中有这个:

        private transient HashMap<E,Object> map;

        默认的构造函数是这个:

        public HashSet() {

        map = new HashMap<>();

        }

        可以看出来Set的本质就是一个Map,只不过这Set中的值就是这个Map中的key值,这个Map中的Value值并没有实际的意义,只是用一个Object来占个位置就好了。逻辑上看成是一个Set类型,实际底层使用Map类型来实现的。当一个元素被放入HashSet中的时候,实际上这个元素是被放入了底层所维护的HashMap的key中,而他们的value值全都是一个Objec对象,并没有什么实际的意义,只起到占位的作用,这个对象在我们对Set进行操作的相关过程中并不会用到。

        实际上只要把HashMap分析清楚就相当于把HashSet一起分析了。

        先看HashSet中维护了静态类:

        static class Entry<K,V> implements Map.Entry<K,V>

        这个类里面维护了一个Key Value键值对的信息,以及其他的相关信息(指向下一个Entry结点的指针)。

        在类的属性中有一个table表,实际上就是一个Entry的数组:

        transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;

        其中EMPTY_TABLE初始的时候是一个空的ENTRY数组:

        static final Entry<?,?>[] EMPTY_TABLE = {};

        具体的代码没有一句一句来分析,只是把主要的思想记录下来,主要是分析在put一个键值对进入的时候具体都执行了哪些操作:

       向HashMap中put一个键值对的时候,先对空间进行检查,如果之前table是空的,就重新为table数组分配空间,之后通过hash函数进行hash地址的计算(这里用到了比较多的移位的操作),就像通常数据结构中介绍的那样,计算出散列地址,之后进行冲突处理。

        这里对于冲突的处理方式是采用链地址法,每次散列到了一个新的地址,就会采用头插法,如果新的键值对与旧的键值对有相同的key值和不同的value值,后面的value值将会把之前的value值替换掉。如果新的键值对与旧的键值对仅仅是hash地址相同但key值是不同的,就是发生的地址冲突的情况,就用头插大将将新的Entry结点插入到当前的位置(最近被调用的再次被调用的几率比较大),当前新的Entry结点的next指针会指向旧的Entry结点。

        在这个过程中还要涉及到table数组的重新复制以及扩容(扩容的阈值是通过之前指定的负载因子来控制的)。

    其他

        可能还有一些框架比如Vector这个基本上被ArrayList代替了,它们实现的功能大致也是相同的,类似地,HashTable也被HashMap代替了,它们也实现的是类似的功能,貌似它们之间主要的区别是线程安全问题,HashTable是线程安全的,其中的许多方法都是Synchonized的HashMap不是线程安全的,但在单线程的环境下,性能比HashTable要好,具体的还没有去深究。

  • 相关阅读:
    Redis为什么要自己实现一个SDS
    Redis中的数据结构
    编程题
    设计模式-代理模式
    设计模式-原型模式
    设计模式-工厂模式(简单工厂,工厂方法,抽象工厂)
    Redis基础
    Windows提高_1.4进程通信
    Windows提高_1.3文件操作
    Windows提高_1.2遍历进程、遍历模块
  • 原文地址:https://www.cnblogs.com/Goden/p/4024798.html
Copyright © 2011-2022 走看看