zoukankan      html  css  js  c++  java
  • java泛型和集合从看懂到看开

                         java泛型和集合详解
            泛型:参数化类型,也就是说所操作的数据类型被指定为一个参数。
                      泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。
                       所有泛型方法声明都有一个类型参数声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前。
                       每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。
                      类型参数可以用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。
                      泛型方法体的声明和其他方法一样。泛型参数不能用基本的数据类型。
                      为什么要使用泛型:意味着编写的代码可以被很多不同类型的对象重用。
                泛型的好处:举个栗子,之前ArrayList类值维护一个Object的数组,这种方法有两个问题。当获取一个值时必须进行强制类型转换。
                比如:ArrayList files=new ArrayList();
                      String filename=(String)files.get();
                     另外还有一个问题,这里不好进入错误检查,也就是说可以向数组列表添加任何类的对象。
                泛型:使得程序具有更好的可读性和安全性。
                定义简单泛型类:语法如下
                         class 类名称 <泛型标识:可以随便写任意标识号,标识指定的泛型的类型>{ private 泛型标识 /*(成员变量类型)*/ var; ..... } }
    例子: 
    public class Pair<A> {//引用类型变量A   泛型类可以有多个类型变量(例如<a,b,c>)
        /**
         * java库中,使用变量E表示集合的元素类型,
         * K和V表示键值,
         * T表示任意类型
         */
        private A first;
        private A second;
        public Pair() {
            // TODO Auto-generated constructor stub
            first=null;
            second=null;
        }
        public A getFirst() {
            return first;
        }
        public A getSecond() {
            return second;
        }
        
        public void setFirst(A first) {
            this.first = first;
        }
        public void setSecond(A second) {
            this.second = second;
        }
    }
    //泛型的类型参数只能是类类型(包括自定义类),不能是简单类型//传入的实参类型需与泛型的类型参数类型相同,即为Integer. 
    Pair<Integer> PairInteger = new Pair<Integer>(123456); 
    //传入的实参类型需与泛型的类型参数类型相同,即为String.
    Pair<String> PairString = new Pair<String>("key_vlaue"); Log.d("泛型测试","key is " + genericInteger.getKey()); Log.d("泛型测试","key is " + genericString.getKey());
    定义的泛型类,就一定要传入泛型类型实参么?
                  并不是这样,在使用泛型的时候如果传入泛型实参,则会根据传入的泛型实参做相应的限制,此时泛型才会起到本应起到的限制作用。如果不传入泛型类型实参的话,在泛型类中使用泛型的方法或成员变量定义的类型可以为任何的类型。
                             
                泛型方法:泛型方法可以写在泛型类中,也可以不写在泛型类中。
                当调用泛型方法的时候,在方法名前的<>放入具体的类型。
                
    public class fxmethod{
             // 泛型方法 printArray                         
               public static < T > void printMethod( E[] PrintArray )
               {
                  // 输出数组元素            
                     for ( T element : PrintArray ){        
                        System.out.printf( "%s ", element );
                     }
                     System.out.println();
                }
             
                public static void main( String args[] )
                {
                    // 创建不同类型数组: Integer, Double 和 Character
                    Integer[] intArray = { 1, 2, 3, 4, 5 };
                    Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };
                    Character[] charArray = { 'H', 'E', 'L', 'L', 'O' };
             
                    System.out.println( "整型数组元素为:" );
                    printMethod(intArray); // 传递一个整型数组
             
                    System.out.println( " 双精度型数组元素为:" );
                    printMethod(doubleArray); // 传递一个双精度型数组
             
                    System.out.println( " 字符型数组元素为:" );
                    printMethod(charArray); // 传递一个字符型数组
                } 
        }
        类型变量的限定:有时类和方法需要对类型变量加以约束,举个栗子,我们要计算数组的最小元素
                        class Arrayfx(){
                            public static <T>T main(T[] a){
            if(a==null || a.length==0)
                return null;
            for(int i=1;i<a.length;i++)
                if(smallest.)
        }
    }
    通配符:允许类型参数实例化。例如:pair(? extends Employee)表示任何泛型pair类型,它的类型参数是Emloyeee的子类,如pair<Manager>,但不是Pair<String>
    import java.util.*; 
          public class GenericTest {
                        public static void main(String[] args) {
                               List<String> name = new ArrayList<String>()
                               List<Integer> age = new ArrayList<Integer>();
                               List<Number> number = new ArrayList<Number>();
                               name.add("icon")
                               age.add(18);  
                               number.add(314);              
                               //getUperNumber(name);
                              //1getUperNumber(age);
                             //2getUperNumber(number);
                            //3}public static void getData(List<?> data) {
                                System.out.println("data :" + data.get(0));
                      }public static void getUperNumber(List<? extends Number> data) {                                   System.out.println("data :" + data.get(0))
    }
    }
     
     
           数组的弊端,一旦数组初始化长度不可变。数组没体验出封装的思想。
           java集合类对象是封装数据的根本。是一个用来存放地域性的容器。
                    用途:最简单的就是前后端数据的交互。如果用最基本的数据类型就无法做到大量的数据交互。
        集合是个大家族,图如下(百度的图)

            集合主要是三大家族,set map 和list
            任何集合都包括三大内容:对外的接口,接口的实现和集合运算的算法。
            Collection接口:该类实现的子接口很多,主要用到的的有三个:List,Set,Queue。接口存储一组不唯一,无序的对象。
            这个接口有两个基本方法add和iterator
            add:方法用于向集合添加元素。成功为true失败为false
            iterator:方法用于返回一个实现了Iterator接口的对象。可以使用这个迭代去对象依此访问集合中的元素。
      public interface Collection<E>{
     boolean add(E element);
     Iterator<E> iterator();
    ...
    }
     boolean add(E element);
     Iterator<E> iterator();
    ...
    }
     Iterator<E> iterator();
    ...
    }
    ...
    }
            迭代器:

                Iterator接口包含3个方法:

    public interface Iterator<E>{
     E next();
     boolean hasNext();
     void remove();
    }

     E next();
     boolean hasNext();
     void remove();
    }

     boolean hasNext();
     void remove();
    }

     void remove();
    }

    }

     E next();
     boolean hasNext();
     void remove();
    }
     boolean hasNext();
     void remove();
    }
     void remove();
    }
    }
     boolean hasNext();
     void remove();
    }
     void remove();
    }
    }
     void remove();
    }
    }
    }

    "for each"循环而已与任何实现了Iterable接口的对象一起工作,换个接口只包含一个方法:


    public interface Iterable<E>{
     Iterator<E> iterator();
    }

    Collection接口扩展了Iterable接口。

    元素被访问的顺序取决于集合类型。如果对 Arraylist进行迭代,迭代器将丛索引0,每迭代一次,索引值加1。然而、如果访回 Hashset中的亓素,每个元素将会按照某种随机的次序出现。虽然可以确定在迭代过程中能够遍历到集合中的所有元素,但却无法预知元素被访问的次序。这对于计算总和或统计符合某个条件的元素个数这类与顺序无关的操作来说,并不是什么问题。

    2.删除元素

    Iterator接口的 remove方法将会删除上次调用next方法时返回的元素。

    3.泛型使用方法

    由于 Collection Iterator都是泛型接口,可以编写操作任何集合类型的实用方法。

    Collection的常用方法
            
    package com.alphabet.Gather;

    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.Iterator;

    public class Collectiona {
        /**
         * Collectiona是接口,所以不能实例化。而ArrayList是addection接口的间接实现类
         * @param args
         */
        public static void main(String[] args) {
            String A="a";
            String B="b";
            String C="c";
            //一次添加一个元素
            Collection<String> add=new ArrayList<String>();
            Collection<String> addAll=new ArrayList<String>();
            add.add(A);
            add.add(B);
            add.add(C);
            //remove(object e);   删除一个指定对象
    //        add.remove(A);
    //        add.remove(B);
    //        add.remove(C);
            System.out.println(add);
            //addAll(addection c);   将一个参数容器中的元素添加到当前容器中
            addAll.addAll(add);
            addAll.add("abcd1");  
            addAll.add("abcd2");  
            addAll.add("abcd3");  
            addAll.add("abcd4");  
            addAll.add("abcd5");  
            // removeAll(Collection c);删除指定的Collection中和本Collection中相同的元素
    //        addAll.removeAll(add);
    //        addAll.remove("abcd1");  
    //        addAll.remove("abcd2");  
    //        addAll.remove("abcd3");  
    //        addAll.remove("abcd4");  
    //        addAll.remove("abcd5"); 
            // 3、清空元素  
    //        addAll.clear(); 
            System.out.println(addAll);
            //contains(object e);  是否包含指定元素
            boolean cpmtains=addAll.retainAll(add);
            System.out.println(cpmtains);
            boolean a=addAll.contains("a");
            System.out.println(a);
            boolean b=addAll.containsAll(add);
            System.out.println(b);
            Iterator<String> it=addAll.iterator();//通过iterator方法序列化集合中的所有对象
            
            String[] stri=new String[1];
            String[] str2=addAll.toArray(stri);//
            for (int i = 0; i < str2.length; i++) {
                System.out.println(str2[i]);
            }
            while(it.hasNext()) {
                String str=it.next();
                System.out.println(str);
            }
        }
    }
     
            List接口实现了List接口以及实现接口的所有实现类。因为List实现了Collection接口,所以,拥有Collection接口的常用方法。又因为List是列表类型。所以,List接口还提供了一些适合自身的常用方法。
            ArrayList:数组实现,查询快,增删慢,轻量级;(线程不安全)
            LinkedList:双向链表实现,增删快,查询慢 (线程不安全)
            Vector:数组实现,重量级  (线程安全、使用少)
                

                    ArrayList自动扩充机制

    实现机制:ArrayList.ensureCapacity(int minCapacity)
    首先得到当前elementData 属性的长度oldCapacity。
    然后通过判断oldCapacity和minCapacity参数谁大来决定是否需要扩容, 如果minCapacity大于 
    oldCapacity,那么我们就对当前的List对象进行扩容。
    扩容的的策略为:取(oldCapacity * 3)/2 + 1和minCapacity之间更大的那个。然后使用数组拷 
    贝的方法,把以前存放的数据转移到新的数组对象中
    如果minCapacity不大于oldCapacity那么就不进行扩容。


                    用LinkedList实现队列:
    队列(Queue)是限定所有的插入只能在表的一端进行,而所有的删除都在表的另一端进行的线性表。
    表中允许插入的一端称为队尾(Rear),允许删除的一端称为队头(Front)。
    队列的操作是按先进先出(FIFO)的原则进行的。
    队列的物理存储可以用顺序存储结构,也可以用链式存储结构。

                    用LinkedList实现栈:
    栈(Stack)也是一种特殊的线性表,是一种后进先出(LIFO)的结构。
    栈是限定仅在表尾进行插入和删除运算的线性表,表尾称为栈顶(top),表头称为栈底(bottom)。
    栈的物理存储可以用顺序存储结构,也可以用链式存储结构。

    package com.alphabet.Gather;

    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.LinkedList;
    import java.util.List;

    public class Lista {
        
        public static void main(String[] args) {
            List<String> list1=new ArrayList<String>();//利用Arraylist类实例化
            List<String> list2=new LinkedList<String>();//利用LinkedList实例化List集合
            String a="a",b="b",c="c",d="d",e="e";
            list1.add(a);
            list1.add(d);
            list1.add(e);
            list1.add(a);
            list1.add(d);
            list1.add(e);
            list1.add(a);
            list1.add(d);
            list1.add(e);
            list1.set(1, b);//将索引位置为1的对象修改为b
            list1.add(3, d);//将d添加到索引位置为3的位置。
            System.out.println(list1);
            Iterator<String>it=list1.iterator();
    //        while (it.hasNext()) {
    //            System.out.println(it.next());
    //        }
    //        for (int i = 0; i < list1.size(); i++) {
    //            System.out.println(list1.get(i));//用get方法获得指定索引位置的对象
    //        }
    //        System.out.println(list1.indexOf(a));
    //        System.out.println(list1.lastIndexOf(a));
    //        System.out.println(list1.indexOf(e));
    //        System.out.println(list1.lastIndexOf(e));
    //        list1=list1.subList(1, 4);
    //        System.out.println(list1+"----------------");
    //        for (int i = 0; i < list1.size(); i++) {
    //            System.out.print(list1.get(i)+"---");
    //        }
            ArrayList<String>list=new ArrayList<String>();
            list.add(0, a);
            list.add(1, b);
            list.add(2, c);
            list.add(3, d);
            list.add(4, e);
            System.out.println(list.get((int) (Math.random()*5)));//用随机数模拟访问集合中的对象
            LinkedList<String>link=new LinkedList<String>();
            link.add(0, a);
            link.add(1, b);
            link.add(2, c);
            link.add(3, d);
            link.add(4, e);
            System.out.println(link.toString());
            System.out.println(link.getFirst());//获取第一个对象
            link.addLast(c);//把c添加到最后一个
            System.out.println(link.getLast());
            link.addFirst(c);//把c添加到第一个
            System.out.println(link.getFirst());
            link.removeFirst();//删除第一个
            System.out.println(link.getLast());
            link.removeLast();//删除最后一个
            System.out.println(link.getLast());
        }
    }
        Set集合:set集合为集类型,集是最简单的一种集合,存放于集合中的对象不按特定方式排序,只是简单地把对象加入集合中,类似向口袋里放东西。不允许存放重复的元素;允许使用null元素
          实现类 :
            HashSet:equals返回true,hashCode返回相同的整数;哈希表;存储的数据是无序的。hashSet的元素值可以为null。
            优点:能快速的定位集合中的元素。
              
    package com.alphabet.Gather;

    import java.util.HashSet;
    import java.util.Iterator;
    import java.util.Set;

    public class Person {
        private String name;
        private long id;
        public Person() {
            super();
            // TODO Auto-generated constructor stub
        }
        public Person(String name, long id) {
            super();
            this.name = name;
            this.id = id;
        }
        @Override
        public String toString() {
            return "Person [name=" + name + ", id=" + id + "]";
        }
        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + (int) (id ^ (id >>> 32));
            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;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public long getId() {
            return id;
        }
        public void setId(long id) {
            this.id = id;
        }
        
        public static void main(String[] args) {
            Set<Person>hashset=new HashSet<Person>();
            hashset.add(new Person("张先生",12530));
            hashset.add(new Person("李先生", 152));
            hashset.add(new Person("aaaa",123));
            Iterator<Person>it=hashset.iterator();
            while (it.hasNext()) {
                Person person = it.next();
                System.out.println(person.getName()+"   "+person.getId());
            }
        }
    }
            LinkedHashSet:是hashset的子类,此实现与 HashSet 的不同之外在于,后者维护着一个运行于所有条目的双重链接列表。存储的数据是有序的。 把上面的代码换成,Set<Person>hashset=new LinkedHashSet<Person>();就可以了。
    public boolean contains(Object o) :如果set包含指定元素,返回true 
    public Iterator iterator()返回set中元素的迭代器 
    public Object[] toArray() :返回包含set中所有元素的数组public Object[] toArray(Object[] a) :返回包含set中所有元素的数组,返回数组的运行时类型是指定数组的运行时类型
    public boolean add(Object o) :如果set中不存在指定元素,则向set加入
    public boolean remove(Object o) :如果set中存在指定元素,则从set中删除 
    public boolean removeAll(Collection c) :如果set包含指定集合,则从set中删除指定集合的所有元素 
    public boolean containsAll(Collection c) :如果set包含指定集合的所有元素,返回true。如果指定集合也是一个set,只有是当前set的子集时,方法返回true
         

            HashSet需要同时通过equals和HashCode来判断两个元素是否相等,具体规则是,如果两个元素通过equals为true,并且两个元素的hashCode相等,则这两个元素相等(即重复)。

           所以如果要重写保存在HashSet中的对象的equals方法,也要重写hashCode方法,重写前后hashCode返回的结果相等(即保证保存在同一个位置)。所有参与计算 hashCode() 返回值的关键属性,都应该用于作为 equals() 比较的标准。

            试想如果重写了equals方法但不重写hashCode方法,即相同equals结果的两个对象将会被HashSet当作两个元素保存起来,这与我们设计HashSet的初衷不符(元素不重复)。

            另外如果两个元素哈市Code相等但equals结果不为true,HashSet会将这两个元素保存在同一个位置,并将超过一个的元素以链表方式保存,这将影响HashSet的效率。

                TreeSet不仅实现了Set接口还实现了SortedSet接口,顾名思义这是一种排序的Set集合,查看jdk源码发现底层是用TreeMap实现的,本质上是一个红黑树原理。 正因为它是排序了的,所以相对HashSet来说,TreeSet提供了一些额外的按排序位置访问元素的方法,例如first(), last(), lower(), higher(), subSet(), headSet(), tailSet().

    TreeSet的排序分两种类型,一种是自然排序,另一种是定制排序。      

                自然排序(在元素中写排序规则)

    TreeSet 会调用compareTo方法比较元素大小,然后按升序排序。所以自然排序中的元素对象,都必须实现了Comparable接口,否则会跑出异常。对于TreeSet判断元素是否重复的标准,也是调用元素从Comparable接口继承而来额compareTo方法,如果返回0则是重复元素(两个元素I相等)。Java的常见类都已经实现了Comparable接口,下面举例说明没有实现Comparable存入TreeSet时引发异常的情况。

                
    package com.alphabet.Gather;

    import java.util.TreeSet;

    public class tree1 implements Comparable<tree1> {
        
        public static void main(String[] args) {
            TreeSet<tree1> tree=new TreeSet<tree1>();
            tree.add(new tree1());
            tree.add(new tree1());
            System.out.println(tree);
            /**
             * 不继承会出现异常
             */
        }

        @Override
        public int compareTo(tree1 o) {
            // TODO Auto-generated method stub
            return 0;
        }
    }

                定制排序(在集合中写排序规则)

    TreeSet还有一种排序就是定制排序,定制排序时候,需要关联一个 Comparator对象,由Comparator提供排序逻辑。下面就是一个使用Lambda表达式代替Comparator对象来提供定制排序的例子。 下面是一个定制排序的列子

                
    package com.alphabet.Gather;

    import java.util.Comparator;
    import java.util.TreeSet;

    public class tree2 {
        private int id;

        public tree2() {
            super();
            // TODO Auto-generated constructor stub
        }

        public tree2(int id) {
            super();
            this.id = id;
        }

        @Override
        public String toString() {
            return "tree2 [id=" + id + "]";
        }

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }
        public static void main(String[] args) {
            @SuppressWarnings("unchecked")
            TreeSet tr=new TreeSet(new Comparator() {
                @Override
                public int compare(Object o1, Object o2) {
                    // TODO Auto-generated method stub
                    tree2 t= (tree2) o1;
                    tree2 t2= (tree2) o2;
                    return t.id>t2.id ?-1:t.id<t2.id ? 1:0;
                }
            });
            tr.add(new tree2(50));
            tr.add(new tree2(30));
            tr.add(new tree2(80));
            System.out.println(tr);
        }
    }

    EnumSet集合:

                

    几种Set的比较:
    HashSet外部无序地遍历成员。 
    成员可为任意Object子类的对象,但如果覆盖了equals方法,同
    时注意修改hashCode方法。 
    TreeSet外部有序地遍历成员; 
    附加实现了SortedSet, 支持子集等要求顺序的操作 
    成员要求实现Comparable接口,或者使用Comparator构造
    TreeSet。成员一般为同一类型。 
    LinkedHashSet外部按成员的插入顺序遍历成员 
    成员与HashSet成员类似 
    HashSet是基于Hash算法实现的,其性能通常都优于TreeSet。我们通常都应该使用HashSet,在我们需要排序的功能时,我们才使用TreeSet。

    HashSet的元素存放顺序和我们添加进去时候的顺序没有任何关系,而LinkedHashSet 则保持元素的添加顺序。TreeSet则是对我们的Set中的元素进行排序存放。

    一般来说,当您要从集合中以有序的方式抽取元素时,TreeSet 实现就会有用处。为了能顺利进行,添加到 TreeSet 的元素必须是可排序的。 而您同样需要对添加到TreeSet中的类对象实现 Comparable 接口的支持。一般说来,先把元素添加到 HashSet,再把集合转换为 TreeSet 来进行有序遍历会更快。

    各种set集合的性能分析

    • HashSet和TreeSet是Set集合中用得最多的I集合。HashSet总是比TreeSet集合性能好,因为HashSet不需要额维护元素的顺序。

    • LinkedHashSet需要用额外的链表维护元素的插入顺序,因此在插入时性能比HashSet低,但在迭代访问(遍历)时性能更高。因为插入的时候即要计算hashCode又要维护链表,而遍历的时候只需要按链表来访问元素。

    • EnumSet元素是所有Set元素中性能最好的,但是它只能保存Enum类型的元素

                Map集合:Map集合为映射类型,映射与集和列表有明显的区别,映射中的每个对象都是成对存在的。其中存储的每个对象都有一个相应的关键字(key),关键字决定了对象在Map中的存储位置。在链接对象的时候必须通过相应的键对象来获取值对象(value)的,每个key 只能映射一个value。
            1、严格来说 Map 并不是一个集合,而是两个集合之间 的映射关系。

        2、这两个集合没每一条数据通过映射关系,我们可以看成是一条数据。即 Entry(key,value)。Map 可以看成是由多个 Entry 组成。

        3、因为 Map 集合即没有实现于 Collection 接口,也没有实现 Iterable 接口,所以不能对 Map 集合进行 for-each 遍历。

            实现类:

        HashMap、TreeMap、LinkedHashMap、Hashtable等
        HashMap:键值对,key不能重复,但是value可以重复;key的实现就是   HashSet;value对应着放;允许null的键或值;
        Hashtable:线程安全的,不允许null的键或值;
        Properties::key和value都是String类型,用来读配置文件;
    TreeMap:对key排好序的Map; key 就是TreeSet, value对应每个key; key要实现Comparable接口或TreeMap有自己的构造器; 
        LinkedHashMap: 此实现与 HashMap 的不同之处在于,后者维护着一个运行于所有条目的双重链接列表。存储的数据是有序的。

            HashMap:
    Map 主要用于存储键(key)值(value)对,根据键得到值,因此键不允许重复,但允许值重复。
    HashMap 是一个最常用的Map,它根据键的HashCode 值存储数据,根据键可以直接获取它的值,具有很快的访问速度。
    HashMap最多只允许一条记录的键为Null;允许多条记录的值为 Null;
    HashMap不支持线程的同步,即任一时刻可以有多个线程同时写HashMap;可能会导致数据的不一致。如果需要同步,可以用 Collections的synchronizedMap方法使HashMap具有同步的能力。
            HashMap实现原理---散列
            Hash哈希算法的意义在于提供了一种快速存取数据的方法,它用一种算法建立键值与真实值之间的对应关系。散列表又称为哈希表。散列表算法的基本思想是:以结点的关键字为自变量,通过一定的函数关系(散列函数)计算出对应的函数值,以这个值作为该结点存储在散列表中地址。
    当散列表中的元素存放太满,就必须进行再散列,将产生一个新的散列表,所有元素存放到新的散列表中,原先的散列表将被删除。在Java语言中,通过负载因子(load factor)来决定何时对散列表进行再散列。例如:如果负载因子0.75,当散列表中已经有75%位置已经放满,那么将进行再散列。
    负载因子越高(越接近1.0),内存的使用效率越高,元素的寻找时间越长。负载因子越低(越接近0.0),元素的寻找时间越短,内存浪费越多。
            Map常用方法:
    put(Object key,Object value):用来存放一个键-值对Map中 
    remove(Object key):根据key(键),移除键-值对,并将值返回
    putAll(Map mapping) :将另外一个Map中的元素存入当前的Map中
    clear() :清空当前Map中的元素
    get(Object key) :根据key(键)取得对应的值
    containsKey(Object key) :判断Map中是否存在某键(key)
    containsValue(Object value):判断Map中是否存在某值(value) 
    keySet() :返回所有的键(key),并使用Set容器存放
    Collection values() :返回所有的值(Value),并使用Collection存放
    entrySet() :返回一个实现 Map.Entry 接口的元素 Set
    size():查看集合中键值关系的个数
    equals:用来查看指定的对象于该对象是否为同一个对象。
    isEmpty:查看集合中是否包含键-值关系。
    package com.alphabet.Gather;

    import java.util.HashMap;
    import java.util.Map;

    public class Mapa {
        public static void main(String[] args) {
            Map<Integer, String> map=new HashMap<Integer, String>();
            map.put(2018, "张三");
            map.put(2017, "李四");
            System.out.println("get()方法的返回结果:");
            System.out.println("-------"+map.get(2018));
            System.out.println("    "+map.get(2017));
            System.out.println("    "+map.get(2019));
            System.out.println("containsKey()方法的返回结果");
            System.out.println("++++++++++"+map.containsKey(2016));
            System.out.println("++++++++++"+map.containsKey(2017));
            System.out.println("++++++++++"+map.containsKey(2018));
            System.out.println("++++++++++"+map.size());
            System.out.println(map.keySet());
            System.out.println(map);
        }
    }
    使用hashMap类
            
    package com.alphabet.Gather;

    public class HashMapa {
        private String Keyfix;
        private int Keynumber;
        private String pk;
        public HashMapa() {
            super();
            // TODO Auto-generated constructor stub
        }
        public HashMapa(String keyfix, int keynumber, String pk) {
            super();
            Keyfix = keyfix;
            Keynumber = keynumber;
            this.pk = pk;
        }
        @Override
        public String toString() {
            return "HashMapa [Keyfix=" + Keyfix + ", Keynumber=" + Keynumber + ", pk=" + pk + "]";
        }
        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((Keyfix == null) ? 0 : Keyfix.hashCode());
            result = prime * result + Keynumber;
            result = prime * result + ((pk == null) ? 0 : pk.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;
            HashMapa other = (HashMapa) obj;
            if (Keyfix == null) {
                if (other.Keyfix != null)
                    return false;
            } else if (!Keyfix.equals(other.Keyfix))
                return false;
            if (Keynumber != other.Keynumber)
                return false;
            if (pk == null) {
                if (other.pk != null)
                    return false;
            } else if (!pk.equals(other.pk))
                return false;
            return true;
        }
        public String getKeyfix() {
            return Keyfix;
        }
        public void setKeyfix(String keyfix) {
            Keyfix = keyfix;
        }
        public int getKeynumber() {
            return Keynumber;
        }
        public void setKeynumber(int keynumber) {
            Keynumber = keynumber;
        }
        public String getPk() {
            return this.Keyfix+"_"+this.getKeynumber();
        }
        public void setPk(String pk) {
            int i=pk.indexOf(" _ ");
            this.Keyfix=pk.substring(0, i);
            this.Keynumber=new Integer(pk.substring(i));
            this.pk = pk;
        }
    }
        
    package com.alphabet.Gather;

    public class persona {
        private String name;
        private HashMapa number;
        
        public persona() {
            super();
            // TODO Auto-generated constructor stub
        }
        public persona(String name, HashMapa number) {
            super();
            this.name = name;
            this.number = number;
        }
        @Override
        public String toString() {
            return "persona [name=" + name + ", number=" + number + "]";
        }
        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((name == null) ? 0 : name.hashCode());
            result = prime * result + ((number == null) ? 0 : number.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;
            persona other = (persona) obj;
            if (name == null) {
                if (other.name != null)
                    return false;
            } else if (!name.equals(other.name))
                return false;
            if (number == null) {
                if (other.number != null)
                    return false;
            } else if (!number.equals(other.number))
                return false;
            return true;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public HashMapa getNumber() {
            return number;
        }
        public void setNumber(HashMapa number) {
            this.number = number;
        };
    }
    package com.alphabet.Gather;

    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.Map;

    public class Test {
    //    private String name;
    //    private int id;
    //    @Override
    //    public String toString() {
    //        // TODO Auto-generated method stub
    //        return "Test[name="+name+",id="+id+"]";
    //    }
        public static void main(String[] args) {
            Map<HashMapa, persona> map=new HashMap<HashMapa, persona> ();
            HashMapa hm=new HashMapa();    //新建值对象
            hm.setKeyfix("MR"); 
            hm.setKeynumber(2011);
            map.put(hm, new persona("张先生",hm));//初始化集合
            HashMapa hm2=new HashMapa(); //新建值对象,内容与上面键对象的内容完全相同
            hm2.setKeyfix("MR"); 
            hm2.setKeynumber(2011);
            persona ps=map.get(hm2); //获得指定键值的映射关系
            if (ps==null) {
                System.out.println("该键对象不存在");
            }else {
                System.out.println(ps.getNumber().getKeynumber()+" "+ps.getName());
            }
        }
    }

            何时需重写equals?

    当一个类有自己特有的“逻辑相等”概念(不同于对象身份的概念);
    Object类仅仅提供了一个对引用的比较,如果两个引用不是同一个那就返回false,这是无法满足大多数对象比较的需要的,所以要覆盖;
    使用==操作符检查实参是否为指向对象的引用”
    使用instanceof操作符检查实参是否为正确的类型
    把实参转换到正确的类型;
    对于该类中每一个“关键”域,检查实参中的域与当前对象中对应的域值是否匹 配。对于既不是float也不是double类型的基本类型的域,可以使用==操作符 进行比较;对于对象引用类型的域,可以递归地调用所引用的对象的equals方法,对于float和double类型的域,先转换成int或long类型的值,然后使用==操作符比较;
    当你编写完成了equals方法之后,应该问自己三个问题:它是否是对称的、传 递的、一致的? 如果答案是否定的,那么请找到 这些特性未能满足的原因,再修改equals方法的代码

            equals()和hashCode()同时覆写
    尤其强调当一个对象被当作键值(或索引)来使用的时候要重写这两个方法;
    覆写equals后,两个不同实例可能在逻辑上相等,但是根据Object.hashCode方法却产生不同的散列码,违反“相等的对象必须具有相等的散列码”。
    导致,当你用其中的一个作为键保存到hashMap、hasoTable或hashSet中,再以“相等的”找另 一个作为键值去查找他们的时候,则根本找不到
    不同类型的hashCode取值
    如果该域是布尔型的,计算(f?0:1)
    如果是char,short,byte或int,计算(int)f
    如果是long类型,计算(int)(f^(f>>>32))
    如果是float类型,计算Float.floatToIntBits(f)
    如果是double类型,计算Dobule.doubleToLongBits(f)
    如果该域是一个对象引用,递归调用hashCode
    如果该域是一个数组,则把每个元素当做单独的域来处理,对每个重要的元素计算一个散列码,

             LinkedHashMap:

                HashMap是一种非常常见、非常有用的集合,但在多线程情况下使用不当会有线程安全问题。

                大多数情况下,只要不涉及线程安全问题,Map基本都可以使用HashMap,不过HashMap有一个问题,就是迭代HashMap的顺序并不是HashMap放置的顺序也就是无序。HashMap的这一缺点往往会带来困扰,因为有些场景,我们期待一个有序的Map。

                这个时候,LinkedHashMap就闪亮登场了,它虽然增加了时间和空间上的开销,但是通过维护一个运行于所有条目的双向链表,LinkedHashMap保证了元素迭代的顺序该迭代顺序可以是插入顺序或者是访问顺序。        

                1、LinkedHashMap可以认为是HashMap+LinkedList,即它既使用HashMap操作数据结构,又使用LinkedList维护插入元素的先后顺序。

                2、LinkedHashMap的基本实现思想就是----多态。可以说,理解多态,再去理解LinkedHashMap原理会事半功倍;反之也是,对于LinkedHashMap原理的学习,也可以促进和加深对于多态的理解。

    package com.alphabet.Gather;
    import java.util.LinkedHashMap;
    public class TreeMapa {
            public static void main(String[] args) {
                LinkedHashMap<String, Integer> lhm = new LinkedHashMap<String, Integer>();
                lhm.put("张三", 23);
                lhm.put("李四", 24);
                lhm.put("赵六", 26);
                lhm.put("王五", 25);
                System.out.println(lhm);
                //{张三=23, 李四=24, 赵六=26, 王五=25}
            }
    }

            TreeMap:TreeMap是一个有序的key-value集合,它是通过红黑树实现的。

            TreeMap 继承于AbstractMap,所以它是一个Map,即一个key-value集合。
            TreeMap 实现了NavigableMap接口,意味着它支持一系列的导航方法。比如返回有序的key集合。
            TreeMap 实现了Cloneable接口,意味着它能被克隆
            TreeMap 实现了java.io.Serializable接口,意味着它支持序列化

       TreeMap中提供了一序列根据key顺序访问key-value的方法

        Map.Entry firstEntry();返回该map中最小的key对应的key-value,若map为空,则返回为null;

        Object firstKey();返回该Map中最小的key值,若map为空,则返回null;

        Map.Entry lastEntry();返回map中最大的key对应的key-value,若map为空,则返回为null;

        Object lastKey();返回该Map中最大的key值,若map为空,则返回null;

        Map.Entry higerEntry(Object key);返回该map中大于指定key的最小的key-value键值对,若map为空,则返回为null;

        Object higherKey(Object key);返回该map中大于指定key的最小的key,若map为空,则返回为null;

        Map.Entry lowerEntry(Object key);返回该map中小于指定key的最大的key-value键值对,若map为空,则返回为null;

        Object lowerKey(Object key);返回该map中小于指定key的最大的key,若map为空,则返回为null;

        NavigableMap subMap(Object fromKey,boolean fromInclusive,object toKey,boolean toInclusive);返回该Map的子Map,他的key的范围是从fomKey(是否包含取决于第二个参数)到toKey(是否包含取决于第四个参数)

        SortMap subMap(Object fromKey,Object toKey);返回Map 的子Map,其Key的范围是从fromKey(包括)到toKey(不包括).

        SortedMap tailMap(Object fromKey):返回该Map的子Map.其key的范围是大于fromKey(包括)的所有的key

        NavigableMap tailMap(Object fromKey,boolean inclusive);返回该Map的子Map.其key的范围是大于fromKey(是否包括取决于第二个参数)的所有的key

        SortedMap tailMap(Object toKey):返回该Map的子Map.其key的范围是小于toKey(包括)的所有的key

        NavigableMap tailMap(Object toKey,boolean inclusive);返回该Map的子Map.其key的范围是小于toKey(是否包括取决于第二个参数)的所有的key

       TreeMap中的key-value对是有序的,所以增加了访问第一个,前一个,后一个,最后一个key-value对的方法,并提供了几个从TreeMap中截取子TreeMap的方法

            

    package com.alphabet.Gather;

    import java.util.Collections;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.TreeMap;

    public class TreeMP {
        public static void main(String[] args) {
            System.out.println("开始:");    
            
            Person person1 = new Person("马先生", 220181);  
            Person person2 = new Person("李先生", 220193);  
            Person person3 = new Person("王小姐", 220186);  
              
            Map<Number, Person> map = new HashMap<Number, Person>();  
            map.put(person1.getId(), person1);  
            map.put(person2.getId(), person2);  
            map.put(person3.getId(), person3);  
              
            // HashMap  
            System.out.println("HashMap,无序:");  
            for (Iterator<Number> it = map.keySet().iterator(); it.hasNext();) {  
                Person person = map.get(it.next());  
                System.out.println(person.getId() + " " + person.getName());  
            }  
              
            // TreeMap  
            System.out.println("TreeMap,升序:");  
            TreeMap<Number, Person> treeMap = new TreeMap<Number, Person>();  
            treeMap.putAll(map);  
            for (Iterator<Number> it = treeMap.keySet().iterator(); it.hasNext();) {  
                Person person = treeMap.get(it.next());  
                System.out.println(person.getId() + " " + person.getName());  
            }  
              
            System.out.println("TreeMap,降序:");  
            TreeMap<Number, Person> treeMap2 =   
                new TreeMap<Number, Person>(Collections.reverseOrder());  
            treeMap2.putAll(map);  
            for (Iterator it = treeMap2.keySet().iterator(); it.hasNext();) {  
                Person person = (Person) treeMap2.get(it.next());  
                System.out.println(person.getId() + " " + person.getName());  
            }  
            System.out.println("结束!");  
        }
    }

            Map集合比较:
    HashMap的存入顺序和输出顺序无关。
    LinkedHashMap 则保留了键值对的存入顺序。
    TreeMap则是对Map中的元素进行排序。
    因为HashMap和LinkedHashMap 存储数据的速度比直接使用TreeMap 要快,存取效率要高。
    当完成了所有的元素的存放后,我们再对整个的Map中的元素进行排序。这样可以提高整个程序的运行的效率,缩短执行时间。
    注意:TreeMap中是根据键(Key)进行排序的。而如果我们要使用TreeMap来进行正常的排序的话,Key 中存放的对象必须实现Comparable 接口。

           
  • 相关阅读:
    fabu dao fenleizhong
    net core 发布到服务器的方式
    暗示net core
    new post
    gg
    asb
    Nodejs 8.0 踩坑经验汇总
    禁止未发布的博文上首页等
    SortedList
    JSON.stringify 语法实例讲解
  • 原文地址:https://www.cnblogs.com/lw687a/p/9535083.html
Copyright © 2011-2022 走看看