第十六天 vector、Set、 Map、 泛型

来源:互联网 发布:网友知乎 编辑:程序博客网 时间:2024/06/03 18:27

1.生成扑克牌的作业使用LinkedList存储一副扑克牌,然后实现洗牌功能。package cn.itcast.list;import java.util.LinkedList;import java.util.Random;//扑克类class Poker{String  color; //花色String num;//点数public Poker(String color, String num) {super();this.color = color;this.num = num;}@Overridepublic String toString() {return "{"+color+num+"}";}}public class Demo1{public static void main(String[] args) {LinkedList pokers = createPoker();shufflePoker(pokers);showPoker(pokers);}//洗牌的功能public static void shufflePoker(LinkedList pokers){//创建随机数对象Random random = new Random();for(int i = 0 ; i <100; i++){ //随机产生两个索引值int index1 = random.nextInt(pokers.size());int index2 = random.nextInt(pokers.size());//根据索引值取出两张牌,然后交换两张牌的顺序Poker poker1 = (Poker) pokers.get(index1);Poker poker2 = (Poker) pokers.get(index2);pokers.set(index1, poker2);pokers.set(index2, poker1);}}//显示扑克牌public static void showPoker(LinkedList pokers){for(int i = 0 ; i<pokers.size() ; i++){System.out.print(pokers.get(i));//换行if(i%10==9){System.out.println();}}}//生成扑克牌的方法public static LinkedList createPoker(){//该集合用于存储扑克对象。LinkedList list = new LinkedList();//定义数组存储所有的花色与点数String[] colors = {"黑桃","红桃","梅花","方块"};String[] nums = {"A","2","3","4","5","6","7","8","9","10","J","Q","K"};for(int i = 0 ; i < nums.length ; i++){for(int j = 0 ; j<colors.length ; j++){list.add(new Poker(colors[j], nums[i]));}}return list;}}2.VectorVector: 描述的是一个线程安全的ArrayList。  ArrayLsit与Vector的区别?相同点: ArrayList与Vector底层都是使用了Object数组实现的。不同点:1. ArrayList是线程不同步的,操作效率高。Vector是线程同步的,操作效率低。2. ArrayList是JDK1.2出现,Vector是jdk1.0的时候出现的。Vector特有的方法: void addElement(E obj)  在集合末尾添加元素 E elementAt( int index) 返回指定角标的元素 Enumeration elements()  返回集合中的所有元素,封装到Enumeration对象中 Enumeration 接口:  boolean hasMoreElements()           测试此枚举是否包含更多的元素。   E nextElement()           如果此枚举对象至少还有一个可提供的元素,则返回此枚举的下一个元素。public static void main(String[] args) {Vector v = new Vector();v.addElement("aaa");v.addElement("bbb");v.addElement("ccc");System.out.println( v );System.out.println( v.elementAt(2) );   // ccc// 遍历Vector遍历Enumeration ens = v.elements();while ( ens.hasMoreElements() ){System.out.println( ens.nextElement() );}}3.Set 接口3.1 setSet:注重独一无二的性质,该体系集合可以知道某物是否已近存在于集合中,不会存储重复的元素用于存储无序(存入和取出的顺序不一定相同)元素,值不能重复。对象的相等性引用到堆上同一个对象的两个引用是相等的。如果对两个引用调用hashCode方法,会得到相同的结果,如果对象所属的类没有覆盖Object的hashCode方法的话,hashCode会返回每个对象特有的序号(java是依据对象的内存地址计算出的此序号),所以两个不同的对象的hashCode值是不可能相等的。如果想要让两个不同的Person对象视为相等的,就必须覆盖Object继下来的hashCode方法和equals方法,因为Object  hashCode方法返回的是该对象的内存地址,所以必须重写hashCode方法,才能保证两个不同的对象具有相同的hashCode,同时也需要两个不同对象比较equals方法会返回true该集合中没有特有的方法,直接继承自Collection。---| Itreable      接口 实现该接口可以使用增强for循环---| Collection描述所有集合共性的接口---| List接口    可以有重复元素的集合                            ---| ArrayList                               ---|  LinkedList---| Set接口无序不可以有重复元素的集合案例:set集合添加元素并使用迭代器迭代元素。import java.util.HashSet;import java.util.Iterator;import java.util.Set;public class Demo2 {public static void main(String[] args) {//Set 集合存和取的顺序不一致。Set hs = new HashSet();hs.add("世界军事");hs.add("兵器知识");hs.add("舰船知识");hs.add("汉和防务");System.out.println(hs);// [舰船知识, 世界军事, 兵器知识, 汉和防务]Iterator it = hs.iterator();while (it.hasNext()) {System.out.println(it.next());}}}3.2 HashSet哈希表边存放的是哈希值。HashSet存储元素的顺序并不是按照存入时的顺序(和List显然不同) 是按照哈希值来存的所以取数据也是按照哈希值取得。存储速度快HashSet不存入重复元素的规则.使用hashcode和equals由于Set集合是不能存入重复元素的集合。那么HashSet也是具备这一特性的。HashSet如何检查重复?HashSet会通过元素的hashcode()和equals方法进行判断元素师否重复。当你试图把对象加入HashSet时,HashSet会使用对象的hashCode来判断对象加入的位置。同时也会与其他已经加入的对象的hashCode进行比较,如果没有相等的hashCode,HashSet就会假设对象没有重复出现。简单一句话,如果对象的hashCode值是不同的,那么HashSet会认为对象是不可能相等的。因此我们自定义类的时候需要重写hashCode,来确保对象具有相同的hashCode值。如果元素(对象)的hashCode值相同,是不是就无法存入HashSet中了? 当然不是,会继续使用equals 进行比较.如果 equals为true 那么HashSet认为新加入的对象重复了,所以加入失败。如果equals 为false那么HashSet 认为新加入的对象没有重复.新元素可以存入.总结:元素的哈希值是通过元素的hashcode方法 来获取的, HashSet首先判断两个元素的哈希值,如果哈希值一样,接着会比较equals方法 如果 equls结果为true ,HashSet就视为同一个元素。如果equals 为false就不是同一个元素。哈希值相同equals为false的元素是怎么存储呢,就是在同样的哈希值下顺延(可以认为哈希值相同的元素放在一个哈希桶中)。也就是哈希一样的存一列。hashtable图1:hashCode值不相同的情况图2:hashCode值相同,但equals不相同的情况。HashSet:通过hashCode值来确定元素在内存中的位置。一个hashCode位置上可以存放多个元素。当hashcode() 值相同equals() 返回为true 时,hashset 集合认为这两个元素是相同的元素.只存储一个(重复元素无法放入)。调用原理:先判断hashcode 方法的值,如果相同才会去判断equals 如果不相同,是不会调用equals方法的。HashSet到底是如何判断两个元素重复。通过hashCode方法和equals方法来保证元素的唯一性,add()返回的是boolean类型判断两个元素是否相同,先要判断元素的hashCode值是否一致,只有在该值一致的情况下,才会判断equals方法,如果存储在HashSet中的两个对象hashCode方法的值相同equals方法返回的结果是true,那么HashSet认为这两个元素是相同元素,只存储一个(重复元素无法存入)。注意:HashSet集合在判断元素是否相同先判断hashCode方法,如果相同才会判断equals。如果不相同,是不会调用equals方法的。HashSet 和ArrayList集合都有判断元素是否相同的方法,boolean contains(Object o)HashSet使用hashCode和equals方法,ArrayList使用了equals方法实现原理:往hashset添加元素的时候,hashet会先调用元素的hashcode方法得到元素的哈希值,然后通过元素的哈希值经过移位等运算,就可以算出该元素在哈希表中存储的位置情况1:如果算出该元素存储的位置目前没有任何元素存储,那么该元素可以直接存储在该位置上。情况2:如果该元素的存储位置目前已经存在有其他元素了,那么会调用该元素的equals方法与该位置的元素再比较一次,如果equals返回的是true,那么该元素与这个位置上的元素就视为重复元素,不允许添加,如果equals方法返回的是false,那么该元素运行添加练习:使用HashSet存储字符串,并尝试添加重复字符串回顾String类的equals()、hashCode()两个方法。import java.util.HashSet;import java.util.Iterator;import java.util.Set; public class Demo43{public static void main(String[] args) {// Set 集合存和取的顺序不一致。Set hs = new HashSet();hs.add("世界军事");hs.add("兵器知识");hs.add("舰船知识");hs.add("汉和防务");// 返回此 set 中的元素的数量System.out.println(hs.size()); // 4// 如果此 set 尚未包含指定元素,则返回 trueboolean add = hs.add("世界军事"); // falseSystem.out.println(add);// 返回此 set 中的元素的数量System.out.println(hs.size());// 4Iterator it = hs.iterator();while (it.hasNext()) {System.out.println(it.next());}}}使用HashSet存储自定义对象,并尝试添加重复对象(对象的重复的判定)import java.util.HashSet;import java.util.Iterator;public class Demo4 {public static void main(String[] args) {HashSet hs = new HashSet();hs.add(new Person("jack", 20));hs.add(new Person("rose", 20));hs.add(new Person("hmm", 20));hs.add(new Person("lilei", 20));hs.add(new Person("jack", 20));Iterator it = hs.iterator();while (it.hasNext()) {Object next = it.next();System.out.println(next);}}}class Person {private String name;private int age;Person() {}public Person(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic int hashCode() {System.out.println("hashCode:" + this.name);return this.name.hashCode() + age * 37;}@Overridepublic boolean equals(Object obj) {System.out.println(this + "---equals---" + obj);if (obj instanceof Person) {Person p = (Person) obj;return this.name.equals(p.name) && this.age == p.age;} else {return false;}}@Overridepublic String toString() {return "Person@name:" + this.name + " age:" + this.age;}}问题:现在有一批数据,要求不能重复存储元素,而且要排序。ArrayList 、 LinkedList不能去除重复数据。HashSet可以去除重复,但是是无序。3.3TreeSetHashCode默认情况下表示的是内存地址,String 类已经重写了Object的hashCode方法了。注意: 如果两个字符串的内容一致,那么返回的hashCode 码肯定也会一致的。String str1 = "hello";String str2 = new String("hello");System.out.println("两个是同一个对象吗?"+(str1==str2));System.out.println("str1的hashCode:"+ str1.hashCode());System.out.println("str2的hashCode:"+ str2.hashCode());案例:使用TreeSet集合存储字符串元素,并遍历import java.util.TreeSet;public class Demo5 {public static void main(String[] args) {TreeSet ts = new TreeSet();ts.add("ccc");ts.add("aaa");ts.add("ddd");ts.add("bbb");System.out.println(ts); // [aaa, bbb, ccc, ddd]}}TreeSet  红-黑树的数据结构,默认对元素进行自然排序(String)。如果在比较的时候两个对象返回值为0,那么元素重复。红-黑树红黑树是一种特定类型的二叉树 红黑树算法的规则: 左小右大。既然TreeSet可以自然排序,那么TreeSet必定是有排序规则的。1:让存入的元素自定义比较规则。2:给TreeSet指定排序规则。方式一:元素自身具备比较性元素自身具备比较性,需要元素实现Comparable接口,重写compareTo方法,也就是让元素自身具备比较性,这种方式叫做元素的自然排序也叫做默认排序。方式二:容器具备比较性当元素自身不具备比较性,或者自身具备的比较性不是所需要的。那么此时可以让容器自身具备。需要定义一个类实现接口Comparator,重写compare方法,并将该接口的子类实例对象作为参数传递给TreeMap集合的构造方法。注意:当Comparable比较方式和Comparator比较方式同时存在时,以Comparator的比较方式为主;注意:在重写compareTo或者compare方法时,必须要明确比较的主要条件相等时要比较次要条件。(假设姓名和年龄一直的人为相同的人,如果想要对人按照年龄的大小来排序,如果年龄相同的人,需要如何处理?不能直接return 0,因为可能姓名不同(年龄相同姓名不同的人是不同的人)。此时就需要进行次要条件判断(需要判断姓名),只有姓名和年龄同时相等的才可以返回0.)通过return 0来判断唯一性。问题:为什么使用TreeSet存入字符串,字符串默认输出是按升序排列的?因为字符串实现了一个接口,叫做Comparable 接口.字符串重写了该接口的compareTo 方法,所以String对象具备了比较性.那么同样道理,我的自定义元素(例如Person类,Book类)想要存入TreeSet集合,就需要实现该接口,也就是要让自定义对象具备比较性.存入TreeSet集合中的元素要具备比较性.比较性要实现Comparable接口,重写该接口的compareTo方法TreeSet属于Set集合,该集合的元素是不能重复的,TreeSet如何保证元素的唯一性通过compareTo或者compare方法中的来保证元素的唯一性。添加的元素必须要实现Comparable接口。当compareTo()函数返回值为0时,说明两个对象相等,此时该对象不会添加进来。比较器接口----| Comparable       compareTo(Object o)     元素自身具备比较性----| Comparator       compare( Object o1, Object o2 )给容器传入比较器TreeSet集合排序的两种方式:一,让元素自身具备比较性。也就是元素需要实现Comparable接口,覆盖compareTo 方法。这种方式也作为元素的自然排序,也可称为默认排序。年龄按照搜要条件,年龄相同再比姓名。import java.util.TreeSet;public class Demo4 {public static void main(String[] args) {TreeSet ts = new TreeSet();ts.add(new Person("aa", 20, "男"));ts.add(new Person("bb", 18, "女"));ts.add(new Person("cc", 17, "男"));ts.add(new Person("dd", 17, "女"));ts.add(new Person("dd", 15, "女"));ts.add(new Person("dd", 15, "女"));System.out.println(ts);System.out.println(ts.size()); // 5}}class Person implements Comparable {private String name;private int age;private String gender;public Person() {}public Person(String name, int age, String gender) {this.name = name;this.age = age;this.gender = gender;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getGender() {return gender;}public void setGender(String gender) {this.gender = gender;}@Overridepublic int hashCode() {return name.hashCode() + age * 37;}public boolean equals(Object obj) {System.err.println(this + "equals :" + obj);if (!(obj instanceof Person)) {return false;}Person p = (Person) obj;return this.name.equals(p.name) && this.age == p.age;}public String toString() {return "Person [name=" + name + ", age=" + age + ", gender=" + gender+ "]";}@Overridepublic int compareTo(Object obj) {Person p = (Person) obj;System.out.println(this+" compareTo:"+p);if (this.age > p.age) {return 1;}if (this.age < p.age) {return -1;}return this.name.compareTo(p.name);}}import java.util.Comparator;import java.util.TreeSet;/* treeSet添加自定义元素: treeSet要注意的事项: 1. 往TreeSet添加元素的时候,如果元素本身具备了自然顺序的特性,那么就按照元素自然顺序的特性进行排序存储。 2. 往TreeSet添加元素的时候,如果元素本身不具备自然顺序的特性,那么该元素所属的类必须要实现Comparable接口,把元素 的比较规则定义在compareTo(T o)方法上。  3. 如果比较元素的时候,compareTo方法返回 的是0,那么该元素就被视为重复元素,不允许添加.(注意:TreeSet与HashCode、equals方法是没有任何关系。)4. 往TreeSet添加元素的时候, 如果元素本身没有具备自然顺序 的特性,而元素所属的类也没有实现Comparable接口,那么必须要在创建TreeSet的时候传入一个 比较器。5.  往TreeSet添加元素的时候,如果元素本身不具备自然顺序的特性,而元素所属的类已经实现了Comparable接口, 在创建TreeSet对象的时候也传入了比较器 那么是以比较器的比较规则优先使用。如何自定义定义比较器: 自定义一个类实现Comparator接口即可,把元素与元素之间的比较规则定义在compare方法内即可。自定义比较器的格式 :class  类名  implements Comparator{  }推荐使用:使用比较器(Comparator)。 */class  Emp implements Comparable<Emp>{int id;String name;int salary;public Emp(int id, String name, int salary) {super();this.id = id;this.name = name;this.salary = salary;}@Overridepublic String toString() {return "{ 编号:"+  this.id+" 姓名:"+ this.name+" 薪水:"+ this.salary+"}";}//@Override //元素与元素之间的比较规则。// 负整数、零或正整数,根据此对象是小于、等于还是大于指定对象。 public int compareTo(Emp o) {//System.out.println(this.name+"compare"+ e.name);return this.salary- o.salary;}}//自定义一个比较器class MyComparator implements Comparator<Emp>{@Overridepublic int compare(Emp o1, Emp o2) {return o1.id-o2.id;}//根据第一个参数小于、等于或大于第二个参数分别返回负整数、零或正整数。 /*@Overridepublic int compare(Object o1, Object o2) {Emp e1 = (Emp) o1;Emp e2 = (Emp) o2;return e1.id - e2.id;}*/}public class Demo6 {public static void main(String[] args) {//创建一个比较器对象MyComparator comparator = new MyComparator();//创建TreeSet的时候传入比较器TreeSet tree = new TreeSet(comparator);tree.add(new Emp(110, "老陆", 100));tree.add(new Emp(113, "老钟", 200));tree.add(new Emp(220, "老汤", 300));tree.add(new Emp(120, "老蔡", 500));System.out.println("集合的元素:"+tree);}}二,让容器自身具备比较性,自定义比较器。需求:当元素自身不具备比较性,或者元素自身具备的比较性不是所需的。那么这时只能让容器自身具备。定义一个类实现Comparator 接口,覆盖compare方法。并将该接口的子类对象作为参数传递给TreeSet集合的构造函数。当Comparable比较方式,及Comparator比较方式同时存在,以Comparator比较方式为主。import java.util.Comparator;import java.util.TreeSet;public class Demo5 {public static void main(String[] args) {TreeSet ts = new TreeSet(new MyComparator());ts.add(new Book("think in java", 100));ts.add(new Book("java 核心技术", 75));ts.add(new Book("现代操作系统", 50));ts.add(new Book("java就业教程", 35));ts.add(new Book("think in java", 100));ts.add(new Book("ccc in java", 100));System.out.println(ts); }}class MyComparator implements Comparator {public int compare(Object o1, Object o2) {Book b1 = (Book) o1;Book b2 = (Book) o2;System.out.println(b1+" comparator "+b2);if (b1.getPrice() > b2.getPrice()) {return 1;}if (b1.getPrice() < b2.getPrice()) {return -1;}return b1.getName().compareTo(b2.getName());}}class Book {private String name;private double price;public Book() {}public String getName() {return name;}public void setName(String name) {this.name = name;}public double getPrice() {return price;}public void setPrice(double price) {this.price = price;}public Book(String name, double price) {this.name = name;this.price = price;}@Overridepublic String toString() {return "Book [name=" + name + ", price=" + price + "]";}}TreeSet实现原理存储原理:底层是用红黑树数(二叉树)据结构实现,存储规则:左小右大import java.util.Comparator;import java.util.TreeSet;/* treeSet添加自定义元素: treeSet要注意的事项:         1. 往TreeSet添加元素的时候,如果元素本身具备了自然顺序的特性,那么就按照元素自然顺序的特性进行排序存储。         2. 往TreeSet添加元素的时候,如果元素本身不具备自然顺序的特性,那么该元素所属的类必须要实现Comparable接口,把元素的比较规则定义在compareTo(T o)方法上。         3. 如果比较元素的时候,compareTo方法返回 的是0,那么该元素就被视为重复元素,不允许添加.(注意:TreeSet与HashCode、equals方法是没有任何关系。)         4. 往TreeSet添加元素的时候, 如果元素本身没有具备自然顺序 的特性,而元素所属的类也没有实现Comparable接口,那么必须要在创建TreeSet的时候传入一个         比较器。         5.  往TreeSet添加元素的时候,如果元素本身不具备自然顺序的特性,而元素所属的类已经实现了Comparable接口, 在创建TreeSet对象的时候也传入了比较器         那么是以比较器的比较规则优先使用。         如何自定义定义比较器: 自定义一个类实现Comparator接口即可,把元素与元素之间的比较规则定义在compare方法内即可。   自定义比较器的格式 :  class  类名  implements Comparator{   }         推荐使用:使用比较器(Comparator)。 */class Emp implements Comparable<Emp> {    int id;    String name;    int salary;    public Emp(int id, String name, int salary) {        super();        this.id = id;        this.name = name;        this.salary = salary;    }    @Override    public String toString() {        return "{ 编号:" + this.id + " 姓名:" + this.name + " 薪水:" + this.salary +        "}";    }    //@Override //元素与元素之间的比较规则。    // 负整数、零或正整数,根据此对象是小于、等于还是大于指定对象。     public int compareTo(Emp o) {        //System.out.println(this.name+"compare"+ e.name);        return this.salary - o.salary;    }}//自定义一个比较器class MyComparator implements Comparator<Emp> {    @Override    public int compare(Emp o1, Emp o2) {        return o1.id - o2.id;    }    //根据第一个参数小于、等于或大于第二个参数分别返回负整数、零或正整数。     /*@Override    public int compare(Object o1, Object o2) {            Emp e1 = (Emp) o1;            Emp e2 = (Emp) o2;            return e1.id - e2.id;    }*/}public class Demo6 {    public static void main(String[] args) {        //创建一个比较器对象        MyComparator comparator = new MyComparator();        //创建TreeSet的时候传入比较器        TreeSet tree = new TreeSet(comparator);        tree.add(new Emp(110, "老陆", 100));        tree.add(new Emp(113, "老钟", 200));        tree.add(new Emp(220, "老汤", 300));        tree.add(new Emp(120, "老蔡", 500));        System.out.println("集合的元素:" + tree);    }}TreeSet是可以对字符串进行排序 的, 因为字符串已经实现了Comparable接口。字符串的比较规则:情况一: 对应位置有不同的字符出现, 就比较的就是对应位置不同的字符。情况 二:对应位置上 的字符都一样,比较的就是字符串的长度。 */public class Demo7 {public static void main(String[] args) {/*TreeSet tree = new TreeSet();tree.add("abcccccccccccccccccc");tree.add("abc");System.out.println(tree);*/System.out.println("abw".compareTo("abcccccccccccc"))}}import java.util.Iterator;import java.util.TreeSet;/*需求:将字符串中的数值进行排序。例如String str="8 10 15 5 2 7"; ---->   "2 5 7 8 10 15"*/public class Demo8 {public static void main(String[] args) {String str="8 10 15 5 2 7";String[] datas = str.split(" ");TreeSet tree = new TreeSet();for(int i = 0 ; i<datas.length ; i++){tree.add(Integer.parseInt( datas[i])); // 字符串转int类型数据是需要使用Integer.parseInt()}//遍历treeSet的元素拼接成对应的字符串Iterator it = tree.iterator();while(it.hasNext()){System.out.print(it.next()+" ");}}}3.4LinkedHashSet会保存插入的顺序。看到array,就要想到角标。看到link,就要想到first,last。看到hash,就要想到hashCode,equals.看到tree,就要想到两个接口。Comparable,Comparator。练习:去除数组中的重复元素.4.Map如果程序中存储了几百万个学生,而且经常需要使用学号来搜索某个学生,那么这个需求有效的数据结构就是Map。Map是一种依照键(key)存储元素的容器,键(key)很像下标,在List中下标是整数。在Map中键(key)可以使任意类型的对象。Map中不能有重复的键(Key),每个键(key)都有一个对应的值(value)。一个键(key)和它对应的值构成map集合中的一个元素。Map中的元素是两个对象,一个对象作为键,一个对象作为值。键不可以重复,但是值可以重复。看顶层共性方法找子类特有对象.Map与Collection在集合框架中属并列存在Map存储的是键值对Map存储元素使用put方法,Collection使用add方法Map集合没有直接取出元素的方法,而是先转成Set集合,在通过迭代获取元素Map集合中键要保证唯一性也就是Collection是单列集合, Map 是双列集合。总结: Map一次存一对元素, Collection 一次存一个。Map 的键不能重复,保证唯一。Map 一次存入一对元素,是以键值对的形式存在.键与值存在映射关系.一定要保证键的唯一性.查看api文档:interface Map<K,V>K - 此映射所维护的键的类型V - 映射值的类型概念将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。特点Key和Value是1对1的关系,如:门牌号 :家  老公:老婆双列集合Map学习体系: ---| Map  接口    将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。---| HashMap  采用哈希表实现,所以无序            ---| TreeMap   可以对健进行排序---|Hashtable:底层是哈希表数据结构,线程是同步的,不可以存入null键,null值。效率较低,被HashMap 替代。---|HashMap:底层是哈希表数据结构,线程是不同步的,可以存入null键,null值。要保证键的唯一性,需要覆盖hashCode方法,和equals方法。---| LinkedHashMap:该子类基于哈希表又融入了链表。可以Map集合进行增删提高效率。---|TreeMap:底层是二叉树数据结构。可以对map集合中的键进行排序。需要使用Comparable或者Comparator 进行比较排序。return 0,来判断键的唯一性。常见方法1、添加:1、V put(K key, V value)    (可以相同的key值,但是添加的value值会覆盖前面的,返回值是前一个,如果没有就返回null)                                          2、putAll(Map<? extends K,? extends V> m)  从指定映射中将所有映射关系复制到此映射中(可选操作)。2、删除1、remove()    删除关联对象,指定key对象2、clear()     清空集合对象3、获取     1:value get(key); 可以用于判断键是否存在的情况。当指定的键不存在的时候,返回的是null。4、判断:1、boolean isEmpty()   长度为0返回true否则false    2、boolean containsKey(Object key)  判断集合中是否包含指定的key3、boolean containsValue(Object value)  判断集合中是否包含指定的value5、长度:Int size()添加:该案例使用了HashMap,建立了学生姓名和年龄之间的映射关系。并试图添加重复的键。import java.util.HashMap;import java.util.Map;public class Demo7{public static void main(String[] args) {// 定义一个Map的容器对象Map<String, Integer > map1 = new HashMap<String, Integer >();map1.put("jack", 20);map1.put("rose", 18);map1.put("lucy", 17);map1.put("java", 25);System.out.println(map1);// 添加重复的键值(值不同),会返回集合中原有(重复键)的值, System.out.println(map1.put("jack", 30)); //20       Map<String, Integer> map2 = new HashMap<String, Integer>();map2.put("张三丰", 100);map2.put("虚竹", 20);System.out.println("map2:" + map2);// 从指定映射中将所有映射关系复制到此映射中。map1.putAll(map2);System.out.println("map1:" + map1);         //}}删除:// 删除:// remove() 删除关联对象,指定key对象// clear() 清空集合对象Map<String, Integer> map1 = new HashMap<String, Integer>();map1.put("jack", 20);map1.put("rose", 18);map1.put("lucy", 17);map1.put("java", 25);System.out.println(map1);// 指定key,返回删除的键值对映射的值。System.out.println("value:" + map1.remove("java"));map1.clear();System.out.println("map1:" + map1);获取:// 获取:// V get(Object key) 通过指定的key对象获取value对象// int size() 获取容器的大小Map<String, Integer> map1 = new HashMap<String, Integer>();map1.put("jack", 20);map1.put("rose", 18);map1.put("lucy", 17);map1.put("java", 25);System.out.println(map1);// V get(Object key) 通过指定的key对象获取value对象// int size() 获取容器的大小System.out.println("value:" + map1.get("jack"));System.out.println("map.size:" + map1.size());判断:// 判断:// boolean isEmpty() 长度为0返回true否则false// boolean containsKey(Object key) 判断集合中是否包含指定的key// boolean containsValue(Object value)Map<String, Integer> map1 = new HashMap<String, Integer>();map1.put("jack", 20);map1.put("rose", 18);map1.put("lucy", 17);map1.put("java", 25);System.out.println(map1);System.out.println("isEmpty:" + map1.isEmpty());System.out.println("containskey:" + map1.containsKey("jack"));System.out.println("containsvalues:" + map1.containsValue(100));遍历Map的方式:1、将map 集合中所有的键取出存入set集合。Set<K> keySet()   返回所有的key对象的Set集合                             再通过get方法获取键对应的值。2、 values() ,获取所有的值.Collection<V> values()不能获取到key对象3、 Map.Entry对象  推荐使用   重点Set<Map.Entry<k,v>> entrySet()将map 集合中的键值映射关系打包成一个对象Map.Entry对象通过Map.Entry 对象的getKey,getValue获取其键和值。第一种方式:使用keySet将Map转成Set集合(keySet()),通过Set的迭代器取出Set集合中的每一个元素(Iterator)就是Map集合中的所有的键,再通过get方法获取键对应的值。import java.util.HashMap;import java.util.Iterator;import java.util.Map;import java.util.Set;public class Demo8{public static void main(String[] args) {Map<Integer, String> map = new HashMap<Integer, String>();map.put(1, "aaaa");map.put(2, "bbbb");map.put(3, "cccc");System.out.println(map);//// 获取方法:// 第一种方式: 使用keySet// 需要分别获取key和value,没有面向对象的思想// Set<K> keySet() 返回所有的key对象的Set集合Set<Integer> ks = map.keySet();Iterator<Integer> it = ks.iterator();while (it.hasNext()) {Integer key = it.next();String value = map.get(key);System.out.println("key=" + key + " value=" + value);}}}第二种方式: 通过values 获取所有值,不能获取到key对象public static void main(String[] args) {Map<Integer, String> map = new HashMap<Integer, String>();map.put(1, "aaaa");map.put(2, "bbbb");map.put(3, "cccc");System.out.println(map);// 第二种方式:// 通过values 获取所有值,不能获取到key对象// Collection<V> values()Collection<String> vs = map.values();Iterator<String> it = vs.iterator();while (it.hasNext()) {String value = it.next();System.out.println(" value=" + value);}}第三种方式: Map.Entrypublic static interface Map.Entry<K,V>通过Map中的entrySet()方法获取存放Map.Entry<K,V>对象的Set集合。Set<Map.Entry<K,V>> entrySet()面向对象的思想将map集合中的键和值映射关系打包为一个对象,就是Map.Entry,将该对象存入Set集合,Map.Entry是一个对象,那么该对象具备的getKey,getValue获得键和值。public static void main(String[] args) {Map<Integer, String> map = new HashMap<Integer, String>();map.put(1, "aaaa");map.put(2, "bbbb");map.put(3, "cccc");System.out.println(map);// 第三种方式: Map.Entry对象 推荐使用 重点// Set<Map.Entry<K,V>> entrySet()// 返回的Map.Entry对象的Set集合 Map.Entry包含了key和value对象Set<Map.Entry<Integer, String>> es = map.entrySet();Iterator<Map.Entry<Integer, String>> it = es.iterator();while (it.hasNext()) {// 返回的是封装了key和value对象的Map.Entry对象Map.Entry<Integer, String> en = it.next();// 获取Map.Entry对象中封装的key和value对象Integer key = en.getKey();String value = en.getValue();System.out.println("key=" + key + " value=" + value);}}4.1 HashMap底层是哈希表数据结构,线程是不同步的,可以存入null键,null值。要保证键的唯一性,需要覆盖hashCode方法,和equals方法。案例:自定义对象作为Map的键。package cn.itcast.gz.map;import java.util.HashMap;import java.util.Iterator;import java.util.Map.Entry;import java.util.Set;public class Demo3 {public static void main(String[] args) {HashMap<Person, String> hm = new HashMap<Person, String>();hm.put(new Person("jack", 20), "1001");hm.put(new Person("rose", 18), "1002");hm.put(new Person("lucy", 19), "1003");hm.put(new Person("hmm", 17), "1004");hm.put(new Person("ll", 25), "1005");System.out.println(hm);System.out.println(hm.put(new Person("rose", 18), "1006"));Set<Entry<Person, String>> entrySet = hm.entrySet();Iterator<Entry<Person, String>> it = entrySet.iterator();while (it.hasNext()) {Entry<Person, String> next = it.next();Person key = next.getKey();String value = next.getValue();System.out.println(key + " = " + value);}}}class Person {private String name;private int age;Person() {}public Person(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic int hashCode() {return this.name.hashCode() + age * 37;}@Overridepublic boolean equals(Object obj) {if (obj instanceof Person) {Person p = (Person) obj;return this.name.equals(p.name) && this.age == p.age;} else {return false;}}@Overridepublic String toString() {return "Person@name:" + this.name + " age:" + this.age;}}}4.2 TreeMapTreeMap的排序,TreeMap可以对集合中的键进行排序。如何实现键的排序?方式一:元素自身具备比较性和TreeSet一样原理,需要让存储在键位置的对象实现Comparable接口,重写compareTo方法,也就是让元素自身具备比较性,这种方式叫做元素的自然排序也叫做默认排序。方式二:容器具备比较性当元素自身不具备比较性,或者自身具备的比较性不是所需要的。那么此时可以让容器自身具备。需要定义一个类实现接口Comparator,重写compare方法,并将该接口的子类实例对象作为参数传递给TreeMap集合的构造方法。注意:当Comparable比较方式和Comparator比较方式同时存在时,以Comparator的比较方式为主;注意:在重写compareTo或者compare方法时,必须要明确比较的主要条件相等时要比较次要条件。(假设姓名和年龄一直的人为相同的人,如果想要对人按照年龄的大小来排序,如果年龄相同的人,需要如何处理?不能直接return 0,以为可能姓名不同(年龄相同姓名不同的人是不同的人)。此时就需要进行次要条件判断(需要判断姓名),只有姓名和年龄同时相等的才可以返回0.)通过return 0来判断唯一性。import java.util.TreeMap;public class Demo4 {public static void main(String[] args) {TreeMap<String, Integer> tree = new TreeMap<String, Integer>();tree.put("张三", 19);tree.put("李四", 20);tree.put("王五", 21);tree.put("赵六", 22);tree.put("周七", 23);tree.put("张三", 24);System.out.println(tree);System.out.println("张三".compareTo("李四"));//-2094}}自定义元素排序import java.util.Comparator;import java.util.Iterator;import java.util.Map.Entry;import java.util.Set;import java.util.TreeMap;public class Demo8{public static void main(String[] args) {TreeMap<Person, String> hm = new TreeMap<Person, String>(new MyComparator());hm.put(new Person("jack", 20), "1001");hm.put(new Person("rose", 18), "1002");hm.put(new Person("lucy", 19), "1003");hm.put(new Person("hmm", 17), "1004");hm.put(new Person("ll", 25), "1005");System.out.println(hm);System.out.println(hm.put(new Person("rose", 18), "1006"));Set<Entry<Person, String>> entrySet = hm.entrySet();Iterator<Entry<Person, String>> it = entrySet.iterator();while (it.hasNext()) {Entry<Person, String> next = it.next();Person key = next.getKey();String value = next.getValue();System.out.println(key + " = " + value);}}}class MyComparator implements Comparator<Person> {@Overridepublic int compare(Person p1, Person p2) {if (p1.getAge() > p2.getAge()) {return -1;} else if (p1.getAge() < p2.getAge()) {return 1;}return p1.getName().compareTo(p2.getName());}}class Person implements Comparable<Person> {private String name;private int age;Person() {}public Person(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic int hashCode() {return this.name.hashCode() + age * 37;}@Overridepublic boolean equals(Object obj) {if (obj instanceof Person) {Person p = (Person) obj;return this.name.equals(p.name) && this.age == p.age;} else {return false;}}@Overridepublic String toString() {return "Person@name:" + this.name + " age:" + this.age;}@Overridepublic int compareTo(Person p) {if (this.age > p.age) {return 1;} else if (this.age < p.age) {return -1;}return this.name.compareTo(p.name);}}注意:Set的元素不可重复,Map的键不可重复,如果存入重复元素如何处理Set元素重复元素不能存入add方法返回falseMap的重复健将覆盖旧键,将旧值返回。5.Collections与Arrays集合框架中的工具类:特点:该工具类中的方法都是静态的。Collections:常见方法:1,对list进行二分查找:前提该集合一定要有序。int binarySearch(list,key);//必须根据元素自然顺序对列表进行升级排序//要求list 集合中的元素都是Comparable 的子类。int binarySearch(list,key,Comparator);2,对list集合进行排序。sort(list); //对list进行排序,其实使用的事list容器中的对象的compareTo方法sort(list,comaprator);//按照指定比较器进行排序3,对集合取最大值或者最小值。max(Collection)max(Collection,comparator)min(Collection)min(Collection,comparator)4,对list集合进行反转。reverse(list);5,对比较方式进行强行逆转。Comparator reverseOrder();Comparator reverseOrder(Comparator);6,对list集合中的元素进行位置的置换。swap(list,x,y);7,对list集合进行元素的替换。如果被替换的元素不存在,那么原集合不变。replaceAll(list,old,new);8,可以将不同步的集合变成同步的集合。Set synchronizedSet(Set<T> s)Map synchronizedMap(Map<K,V> m)List synchronizedList(List<T> list)9. 如果想要将集合变数组:可以使用Collection 中的toArray 方法。注意:是Collection不是Collections工具类传入指定的类型数组即可,该数组的长度最好为集合的size。Arrays:用于对数组操作的工具类1,二分查找,数组需要有序binarySearch(int[])binarySearch(double[])2,数组排序sort(int[])sort(char[])……2,将数组变成字符串。 toString(int[])3,复制数组。 copyOf();4,复制部分数组。copyOfRange():5,比较两个数组是否相同。equals(int[],int[]);6,将数组变成集合。List asList(T[]);这样可以通过集合的操作来操作数组中元素,但是不可以使用增删方法,add,remove。因为数组长度是固定的,会出现UnsupportOperationExcetion。可以使用的方法:contains,indexOf。。。如果数组中存入的基本数据类型,那么asList会将数组实体作为集合中的元素。如果数组中的存入的引用数据类型,那么asList会将数组中的元素作为集合中的元素。import java.util.ArrayList;import java.util.Collections;import java.util.Arrays;import java.util.List;class Demo1 {public static void main(String[] args){ArrayList<Integer> list = new ArrayList<Integer>();list.add(4);list.add(3);list.add(1);list.add(2);list.add(3);// 排序Collections.sort(list);// 折半查找的前提是排序好的元素System.out.println( Collections.binarySearch( list , 8 ) );  // 找不到返回-插入点-1// 反序集合输出Collections.reverse( list );System.out.println( list );// 求最值System.out.println( Collections.max( list ) );   // 4// fill()  使用指定的元素替换指定集合中的所有元素// Collections.fill( list, 5 );System.out.println( list );// 将数组转换为集合Integer is[] = new  Integer[]{6,7,8};List<Integer> list2 =  Arrays.asList(is);list.addAll( list2 );System.out.println( list );// 将List转换为数组Object [] ins =  list.toArray();System.out.println( Arrays.toString( ins ) );   }}集合的练习问题: 定义一个Person数组,将Person数组中的重复对象剔除?思路: 1. 描述一个Person类 2. 将数组转换为Arrays.asList() List 3. Set addAll( list ) 4. hashCode()且equals()import java.util.Arrays;import java.util.Set;import java.util.List;import java.util.HashSet;// 1. 描述Person类class Person {public String name;public int age;public Person() {}public Person(String name, int age) {this.name = name;this.age = age;}public String toString() {return getClass().getName() + " : name=" + this.name + " age="+ this.age;}// 4. 重写hashCode和equals()public int hashCode() {return this.age;}public boolean equals(Object o) {Person p = null;if (o instanceof Person)p = (Person) o;return this.name.equals(p.name) && (this.age == p.age);}}class Demo2 {public static void main(String[] args) {Person[] ps = new Person[] { new Person("jack", 34),new Person("lucy", 20), new Person("lili", 10),new Person("jack", 34) };// 遍历数组System.out.println(Arrays.toString(ps));// 2. 将自定义对象数组转换为List集合List<Person> list = Arrays.asList(ps);// 3. 将List转换为SetSet<Person> set = new HashSet<Person>();set.addAll(list);System.out.println(set);}}6.泛型(Generic)把一个集合中元素全部转成大写public class Demo1 {public static void main(String[] args) {ArrayList<String>  list = new ArrayList<String>();  //<String> 表示该容器只能存储字符串类型 的数据。list.add("aa");list.add("bb");list.add("cc");for(int i = 0 ; i < list.size() ; i++){String str =  list.get(i);System.out.println("大写:"+ str.toUpperCase());}MyUtil.print(list);ArrayList<String> list2 = MyUtil.getList();}}泛型的好处:  1. 将运行时的异常提前至了编译时。  2. 避免了无谓的强制类型转换 。泛型在集合中的常见应用:  ArrayList<String>  list = new ArrayList<String>();  true     推荐使用。  ArrayList<Object>  list = new ArrayList<String>();  false  ArrayList<String>  list = new ArrayList<Object>();  false  //以下两种写法主要是为了兼顾新老系统的兼用性问题。    ArrayList<String>  list = new ArrayList();           true       ArrayList    list = new ArrayList<String>();   true   注意: 泛型没有多态的概念,左右两边的数据 类型必须 要一致,或者只是写一边的泛型类型。推荐使用: 两边都写泛型。当集合中存储的对象类型不同时,那么会导致程序在运行的时候的转型异常import java.util.ArrayList;import java.util.Iterator;public class Demo5 {public static void main(String[] args) {ArrayList arr = new ArrayList();arr.add(new Tiger("华南虎"));arr.add(new Tiger("东北虎"));arr.add(new Sheep("喜羊羊"));System.out.println(arr);Iterator it = arr.iterator();while (it.hasNext()) {Object next = it.next();Tiger t = (Tiger) next;t.eat();}}}class Tiger {String name;public Tiger() {}public Tiger(String name) {this.name = name;}@Overridepublic String toString() {return "Tiger@name:" + this.name;}public void eat() {System.out.println(this.name + "吃羊");}}class Sheep {String name;public Sheep() {}public Sheep(String name) {this.name = name;}@Overridepublic String toString() {return "Sheep@name:" + this.name;}public void eat() {System.out.println(this.name + "吃青草");}}原因 :发现虽然集合可以存储任意对象,但是如果需要使用对象的特有方法,那么就需要类型转换,如果集合中存入的对象不同,可能引发类型转换异常.[Tiger@name:华南虎, Tiger@name:东北虎, Sheep@name:喜羊羊]华南虎吃羊东北虎吃羊Exception in thread "main" java.lang.ClassCastException: cn.itcast.gz.map.Sheep cannot be cast to cn.itcast.gz.map.Tigerat cn.itcast.gz.map.Demo5.main(Demo5.java:17)出现问题:存入的是特定的对象,取出的时候是Object对象,需要强制类型转换,可能诱发类型转换异常.无法控制存入的是什么类型的对象,取出对象的时候进行强转时可能诱发异常.而且在编译时期无法发现问题.虽然可以再类型转换的时候通过if语句进行类型检查(instanceof),但是效率较低.(例如吃饭的时候,还需要判断米饭里有没有沙子,吃饭效率低).可以通过给容器加限定的形式规定容器只能存储一种类型的对象.就像给容器贴标签说明该容器中只能存储什么样类型的对象。所以在jdk5.0后出现了泛型泛型应用:格式1.集合类<类类型>  变量名  = new  集合类<类类型>();public class Demo5 {public static void main(String[] args) {// 使用泛型后,规定该集合只能放羊,老虎就进不来了.ArrayList<Sheep> arr = new ArrayList<Sheep>();arr.add(new Sheep("美羊羊"));arr.add(new Sheep("懒洋洋"));arr.add(new Sheep("喜羊羊"));// 编译失败// arr.add(new Tiger("东北虎"));System.out.println(arr);Iterator<Sheep> it = arr.iterator();while (it.hasNext()) {// 使用泛型后,不需要强制类型转换了Sheep next = it.next();next.eat();}}}1. 将运行时的异常提前至编译时发生。2. 获取元素的时候无需强转类型,就避免了类型转换的异常问题格式  通过<> 来指定容器中元素的类型.什么时候使用泛型:当类中操作的引用数据类型不确定的时候,就可以使用泛型类.JDK5.0之前的Comparablepackage java.lang;public interface Comparable {    public int compareTo(Object o);}JDK5.0之后的Comparablepackage java.lang;public interface Comparable<T> {    public int compareTo(T o);}这里的<T>表示泛型类型,随后可以传入具体的类型来替换它.细节一声明好泛型类型之后,集合中只能存放特定类型元素public class Demo6 {public static void main(String[] args) {//创建一个存储字符串的listArrayList<String> arr=new ArrayList<String>();arr.add("gz");arr.add("itcast");//存储非字符串编译报错.arr.add(1);}}细节二:泛型类型必须是引用类型public class Demo6 {public static void main(String[] args) {// 泛型类型必须是引用类型,也就是说集合不能存储基本数据类型// ArrayList<int> arr2=new ArrayList<int>();// 使用基本数据类型的包装类ArrayList<Integer> arr2 = new ArrayList<Integer>();}}细节三: 使用泛型后取出元素不需要类型转换.public class Demo6 {public static void main(String[] args) {ArrayList<String> arr = new ArrayList<String>();arr.add("gzitcast");arr.add("cditcast");arr.add("bjitcast");//使用泛型后取出元素不需要类型转换.String str=arr.get(0);System.out.println();}}6.1泛型方法需求:写一个函数,调用者传递什么类型的变量,该函数就返回什么类型的变量?实现一:由于无法确定具体传递什么类型的数据.那么方法的形参就定义为Object类型.返回值也就是Object类型.但是使用该函数时需要强制类型转换.private Object getDate(Object obj) {return obj;}当不进行强制类型转换能否写出该功能.?目前所学的知识无法解决该问题就需要使用泛型类解决使用的泛型的自定义来解决以上问题。泛型: 就是将类型当作变量处理。规范泛型的定义一般是一个大写的任意字母。1. 函数上的泛型定义      当函数中使用了一个不明确的数据类型,那么在函数上就可以进行泛型的定义。  public <泛型的声明> 返回值类型  函数名( 泛型 变量名  ){   }需求: 定义一个方法可以接收任意类型的参数,而且返回值类型必须 要与实参的类型一致。 自定义泛型:  自定义泛型就是一个数据类型的占位符或者是一个数据类型的变量。方法上自定义泛型:修饰符  <声明自定义的泛型>返回值类型    函数名(使用自定义泛型 ...){  }例如编写一个数组的工具类在泛型中不能使用基本数据类型,如果需要使用基本数据类型,那么就使用基本数据类型对应的包装类型。 byte----> Byte short---> Short  int----> Integer long----> Long  double ----> Double  float -----> Float boolean-----Boolean char-------》 Character 方法泛型注意的事项: 1. 在方法上自定义泛型,这个自定义泛型的具体数据类型是在调用该 方法的时候传入实参时确定具体的数据类型的。 2. 自定义泛型只要符合标识符 的命名规则即可, 但是自定义泛型我们一般都习惯使用一个大写字母表示。  T Type  E Element  */public class Demo2 {public static void main(String[] args) {String str = getData("abc");Integer i = getData(123);}public static <abc>abc getData(abc o){return o;}}细节:使用泛型方法前需要进行泛型声明,使用一对尖括号 <泛型>,声明的位置在static后返回值类型前。当一个类中有多个函数声明了泛型,那么该泛型的声明可以声明在类上。6.2泛型类 格式2. 类上的泛型声明      修饰符 class 类名<泛型>{        }泛型类要注意的事项: 1. 在类上自定义泛型的具体数据类型是在使用该类的时候创建对象时候确定的。 2. 如果一个类在类上已经声明了自定义泛型,如果使用该类创建对象 的时候没有指定 泛型的具体数据类型,那么默认为Object类型 3.在类上自定义泛型不能作用于静态的方法,如果静态的方法需要使用自定义泛型,那么需要在方法上自己声明使用。import java.util.Arrays;public class Demo6<T> {public static void main(String[] args) {// 使用泛型类,创建对象的时候需要指定具体的类型new Demo6<Integer>().getData(5);}public T getData(T data) {return data;}// 反序任意类型数组public void reverse(T[] arr) {int start = 0;int end = arr.length - 1;for (int i = 0; i < arr.length; i++) {if (start < end) {T temp = arr[start];arr[start] = arr[end];arr[end] = temp;}}}class MyArrays<T>{//元素翻转public void reverse(T[] arr){for(int startIndex = 0, endIndex = arr.length-1 ; startIndex<endIndex ; startIndex++,endIndex--){T temp  = arr[startIndex];arr[startIndex] = arr[endIndex];arr[endIndex] = temp;}}//public String toString(T[] arr){StringBuilder sb = new StringBuilder();for(int i = 0 ; i < arr.length ; i++){if(i==0){sb.append("["+arr[i]+",");}else if(i==arr.length-1){sb.append(arr[i]+"]");}else{sb.append(arr[i]+",");}}return sb.toString();}public static <T>void print(T[] t){}}public class Demo3 {public static void main(String[] args) {Integer[] arr = {10,12,14,19};MyArrays<Integer> tool = new MyArrays<Integer>();tool.reverse(arr);System.out.println("数组的元素:"+tool.toString(arr));MyArrays<String> tool2 = new MyArrays<String>();String[] arr2 = {"aaa","bbb","ccc"};tool2.reverse(arr2);ArrayList<String> list = new ArrayList<String>();}}在泛型类中定义一个静态方法public class Demo6<T> {public static void main(String[] args) {System.out.println(getData2(100));}public T getData(T data) {return data;}//静态方法public static T getData2(T data) {return data;}}注意:静态方法不可以使用类中定义的泛型因为类中的泛型需要在对象初始化时指定具体的类型,而静态优先于对象存在。那么类中的静态方法就需要单独进行泛型声明,声明泛型一定要写在static后,返回值类型之前泛型类细节:1、创建对象的时候要指定泛型的具体类型2、创建对象时可以不指定泛型的具体类型(和创建集合对象一眼)。默认是Object,例如我们使用集合存储元素的时候没有使用泛型就是那么参数的类型就是Object3、类上面声明的泛型只能应用于非静态成员函数,如果静态函数需要使用泛型,那么需要在函数上独立声明。4、如果建立对象后指定了泛型的具体类型,那么该对象操作方法时,这些方法只能操作一种数据类型。5、所以既可以在类上的泛型声明,也可以在同时在该类的方法中声明泛型。泛型练习:定义泛型成员public class Demo7 {public static void main(String[] args) {Father<String> f = new Father<String>("jack");System.out.println(f.getT());Father<Integer> f2 = new Father<Integer>(20);System.out.println(f2.getT());}}class Father<T> {private T t;public Father() {}public Father(T t) {super();this.t = t;}public T getT() {return t;}public void setT(T t) {this.t = t;}}如果Father类有子类,子类该如何实现public class Demo7 {public static void main(String[] args) {Father<String> f = new Father<String>("jack");System.out.println(f.getT());Father<Integer> f2 = new Father<Integer>(20);System.out.println(f2.getT());}}class Father<T> {private T t;public Father() {}public Father(T t) {super();this.t = t;}public T getT() {return t;}public void setT(T t) {this.t = t;}}//子类指定了具体的类型class Son extends Father<String>{}//子类也需要使用泛型class Son3<T> extends Father<T>{}//错误写法,父类上定义有泛型需要进行处理class Son2 extends Father<T>{}6.3泛型接口泛型接口的定义格式: interface 接口名<声明自定义泛型>{}泛型接口要注意的事项:1. 接口上自定义的泛型的具体数据类型是在实现一个接口的时候指定 的。2. 在接口上自定义的泛型如果在实现接口的时候没有指定具体的数据类型,那么默认为Object类型。需求: 目前我实现一个接口的时候,我还不明确我目前要操作的数据类型,我要等待创建接口实现类 对象的时候我才能指定泛型的具体数据类型。如果要延长接口自定义泛型 的具体数据类型,那么格式如下:格式:  public class Demo4<T> implements Dao<T>{}*/interface Dao<T>{public void add(T t);}public class Demo4<T> implements Dao<T> {public static void main(String[] args) {Demo4<String> d = new Demo4<String>();}public void add(T t){}}6.4 泛型通配符需求:定义一个方法,接收一个集合对象(该集合有泛型),并打印出集合中的所有元素。例如集合对象如下格式:Collection<Person> coll = new ArrayList<Person>();coll.add(new Person("jack", 20));coll.add(new Person("rose", 18));Collection<Object> coll2 = new ArrayList<Object>();coll2.add(new Object());coll2.add(new Object());coll2.add(new Object());Collection<String> coll3 = new ArrayList<String>();coll3.add("abc");coll3.add("ddd");coll3.add("eee");分析,集合对象中的元素的类型是变化的,方法的形参的那么泛型类型就只能定义为Object类型.import java.util.ArrayList;import java.util.Collection;import java.util.HashSet;import java.util.Iterator;public class Demo9 {public static void main(String[] args) {ArrayList<Object> arr = new ArrayList<Object>();arr.add(new Object());arr.add("String");print(arr);//将集合的泛型设置类String类型,是Object子类HashSet<String> hs = new HashSet<String>();hs.add("hello");hs.add("jack");//由于print方法接收的集合进行了元素限定,只接受限定为Object类型的集合,编译不通过//print(hs);}public static void print(Collection<Object> coll) {Iterator<Object> it = coll.iterator();while (it.hasNext()) {Object next = it.next();System.out.println(next);}}}但是,由于print方法接收的集合进行了元素限定,只接受限定为Object类型的集合,编译不通过该问题如何解决?可以把方法的形参的泛型去掉,那么方法中就把集合中的元素当做Object类型处理.也可以使用使用泛型通配符public class Demo9 {public static void main(String[] args) {ArrayList<Object> arr = new ArrayList<Object>();arr.add(new Object());arr.add("String");print(arr);// 将集合的泛型设置类String类型,是Object子类HashSet<String> hs = new HashSet<String>();hs.add("hello");hs.add("jack");// 使用泛型通配符,编译通过。print(hs);}public static void print(Collection<?> coll) {Iterator<?> it = coll.iterator();while (it.hasNext()) {Object next = it.next();System.out.println(next);}}}上述就使用了泛型通配符通配符:?public void show(List<?> list){}可以对类型进行限定范围。?extends E: 接收E类型或者E的子类型。? super E: 接收E类型或者E的父类型。限定泛型通配符的边界限定通配符的上边界:extends 接收Number 类型或者Number的子类型正确:Vector<? extends Number> x = new Vector<Integer>();错误:Vector<? extends Number> x = new Vector<String>();限定通配符的下边界super接收Integer 或者Integer的父类型正确:Vector<? super Integer> x = new Vector<Number>();错误:Vector<? super Integer> x = new Vector<Byte>();import java.util.ArrayList;import java.util.Collection;import java.util.HashSet;/* 泛型的上下限:需求1: 定义一个函数可以接收接收任意类型的集合对象, 要求接收的集合对象只能存储Integer或者是Integer的父类类型数据。需求2: 定义一个函数可以接收接收任意类型的集合对象, 要求接收的集合对象只能存储Number或者是Number的子类类型数据。泛型中通配符: ? ? super Integer : 只能存储Integer或者是Integer父类元素。  泛型 的下限? extends Number : 只能存储Number或者是Number类型的子类数据。 泛型上限*/public class Demo5 {public static void main(String[] args) {ArrayList<Integer> list1 = new ArrayList<Integer>();ArrayList<Number> list2 = new ArrayList<Number>();HashSet<String> set = new HashSet<String>();//getData(set);}//泛型的上限public static void getData(Collection<? extends Number> c){}//泛型的下限public static void print(Collection<? super Integer> c){}}总结:JDK5中的泛型允许程序员在编写集合代码时,就限制集合的处理类型,从而把原来程序运行时可能发生问题,转变为编译时的问题,以此提高程序的可读性和稳定注意:泛型是提供给javac编译器使用的,它用于限定集合的输入类型,让编译器在源代码级别上,即挡住向集合中插入非法数据。但编译器编译完带有泛形的java程序后,生成的class文件中将不再带有泛形信息,以此使程序运行效率不受到影响,这个过程称之为“擦除”。泛型的基本术语,以ArrayList<E>为例:<>念着typeofArrayList<E>中的E称为类型参数变量ArrayList<Integer>中的Integer称为实际类型参数整个称为ArrayList<E>泛型类型整个ArrayList<Integer>称为参数化的类型ParameterizedType最后:关于数据结构可以查看如下网站:http://www.cs.armstrong.edu/liang/animation/index.html   数据结构


0 0
原创粉丝点击