泛型:集合中存储了不同类型的对象,取出时,容易在运行时时期发生ClassCastException类型转换异常。
例子1.集合中存储不同类型时,出现错误。
package cn.itheima.day12; import java.util.ArrayList; import java.util.Iterator; public class GenericDemo { public static void main(String[] args) { ArrayList al = new ArrayList(); al.add("haha"); al.add("hehe"); al.add(new Integer(3)); Iterator it = al.iterator(); while(it.hasNext()){ String s = (String)it.next(); System.out.println(s+"::"+s.length()); } } }
为了避免这个问题的发生。如果在存储的时候就明确了集合要操作的数据类型,这样取出就没有问题了,就需要在定义集合时,就立刻明确元素的类型。其实借鉴于数组。可以通过<>来明确元素的类型。
例子2:使用泛型,改变例子1,使得错误避免。
package cn.itheima.day12; import java.util.ArrayList; import java.util.Iterator; public class GenericDemo { public static void main(String[] args) { ArrayList<String> al = new ArrayList<String>(); al.add("haha"); al.add("hehe"); Iterator<String> it = al.iterator(); while(it.hasNext()){ String s = it.next(); System.out.println(s+"::"+s.length()); } } }
泛型的好处: 1,将运行时期出现的ClassCastException问题,转移到了编译时期。 2,避免了强制转换的麻烦。 泛型其实是JDK1.5版本以后出现的一个安全机制。泛型其实给编译器使用的。泛型的表现形式就是<>。 编码时,什么时候使用泛型呢? 在API中,只要用到的类或者接口的旁边有<>时,就要明确具体类型。泛型的使用其实就是给<>传递实际参数,而这个参数就是一个具体引用数据类型。
例子3.使用泛型,完成自定义比较器的定义,使得自定义的Person按照姓名来排序。
package cn.itheima.day12; import java.util.Comparator; import java.util.Iterator; import java.util.TreeSet; /** * 现在要对Person对象按照年龄进行排序,使用TreeSet集合 * @author wl-pc */ public class GenericDemo2 { public static void main(String[] args) { TreeSet<Person4> ts = new TreeSet<Person4>(new CompareByName1()); ts.add(new Person4("lisi1", 21)); ts.add(new Person4("lisi3", 23)); ts.add(new Person4("lisi4", 24)); ts.add(new Person4("lisi2", 22)); ts.add(new Person4("lisi6", 26)); //System.out.println(ts); Iterator<Person4> it = ts.iterator(); while(it.hasNext()){ Person4 p = it.next(); System.out.println(p.getName()+"---"+p.getAge()); } } } //自定义一个比较器(按照name排序) class CompareByName1 implements Comparator<Person4>{ @Override public int compare(Person4 p1, Person4 p2) { int num = p1.getName().compareTo(p2.getName()); return num==0?p1.getAge()-p2.getAge():num; } } //默认的比较器(按照年龄排序) class Person4 implements Comparable<Person4>{ private String name; private int age; public Person4(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public int getAge() { return age; } @Override public String toString() { return name+"----"+age; } //复写equals()方法 //注意:这个地方是不能使用泛型的。 public boolean equals(Object obj){ if(this==obj) return true; if(!(obj instanceof Person4)) return false; Person4 p = (Person4)obj; return this.name.equals(p.name) && this.age==p.age; } //复写hashCode()方法 public int hashCode() { final int NUM = 38; return name.hashCode() + age*NUM; }; @Override public int compareTo(Person4 p) { int temp = this.age - p.age; return temp==0?this.name.compareTo(p.name):temp; } }
什么时候使用泛型类呢? 当类要操作的引用数据类型不确定的时候,可以使用泛型来定义,也就是定义一个类型参数。具体要操作什么类型的对象,有使用该类的使用者来明确,将具体的类型做为实际参数传递给<>.
例子4,泛型类的使用实例演示。
package cn.itheima.day12; class Student{ private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public Student(String name) { super(); this.name = name; } } //使用泛型定义一个工具类 //使用泛型解决安全隐患 //将泛型定义到类上,在该类中都有效。 class Tool<T>{ private T t; public void setT(T t){ this.t = t; } public T getT(){ return t; } } public class GenericDemo3 { public static void main(String[] args) { Tool<Student> t = new Tool<Student>(); t.setT(new Student()); Student s = t.getT(); } }
当泛型定义在类上,该泛型作用于整个类。当该建立对象时,就明确了具体类型。那么凡是使用了类上定义的泛型的方法,操作的类也就固定了。
例子5:希望类中的方法,可以操作任意类型而不受类中泛型限制。 解决方法:可以将泛型定义在方法上。也就是泛型方法。
package cn.itheima.day12;
class Tool1<T>{
public void show(T t){
System.out.println("show:"+t);
}
public <Q> void print(Q q){
System.out.println("print:"+q);
}
//当类中定义静态方法时,静态方法时不可以访问类上的方法的
//因为类上的泛型只有建立对象才可以明确具体的类型。所以
//静态方法如果操作的引用数据类型不确定,只能讲泛型定义到方法上。
public static <W> void method(W w){
System.out.println("method:"+w);
}
}
public class GenericDemo4 {
public static void main(String[] args) {
Tool1<String> t = new Tool1<String>();
t.show("haha");
t.print(new Integer(1));
t.method("asc");
t.method(2);
}
}
当类中定义static方法时,静态方法是不可以直接方位类上的泛型,因为类上的泛型只有通过建立对象才可以明确具体类型。 所以静态方法如果操作的引用数据类型不确定,只能将泛型定义在方法上。 在静态方法上定义泛型,必须定义在static关键字之后。 例如:public static <W> void method(W w){ System.out.println("method:"+w); } 当方法中操作的应用数据类型不确定,而且和对应的对象执行的类型也不一定一致。这时就将泛型定义在方法上。
例子6,泛型接口实例。
package cn.itheima.day12; /** * 定义泛型接口。 * @author wl-pc */ interface Inter<T>{ public void show(T t); } class InterImpl<T> implements Inter<T>{ @Override public void show(T t) { System.out.println("t:"+t); } } public class GenericDemo5 { public static void main(String[] args) { Inter<String> t = new InterImpl<String>(); t.show("hehe"); } }
例子7 泛型:通配符:?实例演示。
package cn.itheima.day12; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; /** * 泛型:通配符。? 代表任意类型。 * @author wl-pc * Collection : boolean containsAll(Collection<?> c):判断一个集合中是否包含另一个集合中的元素。 //这时不用boolean containsAll(Collection<T> c),因为这里要用T了, //则下面的也要用T,这时就不能判断不同类型的集合了。所以不确定类型时用?号声明。 //如果用T就声明了一个具体类型的变量。 //containsAll底层用的方法就是equals。 "ABC".equals(new Integer(3)); -->return false; class MyCollection<T> { boolean containsAll(Collection<?> c) { } } */ public class GenericDemo6 { public static void main(String[] args) { ArrayList<String> al = new ArrayList<String>(); al.add("abc1"); al.add("abc2"); al.add("abc3"); HashSet<Integer> hs = new HashSet<Integer>(); hs.add(1); hs.add(3); hs.add(4); show(al); show(hs); } public static void show(Collection<?> coll){ Iterator<?> it = coll.iterator(); while(it.hasNext()){ System.out.println(it.next()); } } }
传入的集合有一个特点,就是元素类型,要么是Person,要么是Person的子类型。这时可以使用泛型的高级应用,泛型的限定。 在泛型方法中,不可以使用具体类型的方法,最多只能使用Object类中方法。 定义T只能固定一种类型,定义?可以是任意类型。若只想操作Person或者Person的子类类型。使用泛型的限定。? extends E:接收E类型或者E的子类型。 泛型的限定: ? extends E:接收E类型或者E的子类型。 ? super E:接收E类型或者E的父类型。
例子8. 若只想操作Person或者Person的子类类型。使用泛型的限定。
package cn.itheima.day12; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; class Person5{ private String name; Person5(String name){ this.name = name; } public String getName(){ return name; } } class Student2 extends Person5{ public Student2(String name) { super(name); } } public class GenericDemo7 { public static void main(String[] args){ ArrayList<Person5> al =new ArrayList<Person5>(); al.add(new Person5("p1")); al.add(new Person5("p2")); al.add(new Person5("p3")); //定义集合要保证左右两边的类型一致。 //两边只有一边定义泛型,也是可以支持,至少新老版本兼容。但是一样会出现,安全提示信息。 ArrayList<Student2> als = new ArrayList<Student2>(); als.add(new Student2("s1")); als.add(new Student2("s2")); als.add(new Student2("s3")); show(al); show(als); } /** * 传入的集合有一个特点,就是元素类型,要么是Person,要么是Person的子类型。 这时可以使用泛型的高级应用,泛型的限定。 在泛型方法中,不可以使用具体类型的方法比如:getName(),最多只能使用Object类中方法。 定义T只能固定一种类型,定义?可以是任意类型。若只想操作Person或者Person的子类类型。 使用泛型的限定。? extends E:接收E类型或者E的子类型。 泛型的限定: ? extends E:接收E类型或者E的子类型。 ? super E:接收E类型或者E的父类型。 比较时,只要是当前的类型或者当前类型的父类型,就用? super E 当往里面存入对象时,接收的是指定的类型或者这个类型的子类型,就用? extends E * @param coll */ //使用泛型定义show()方法,显示集合信息 public static void show(Collection<? extends Person5> coll){ Iterator<? extends Person5> it = coll.iterator(); while(it.hasNext()){ System.out.println(it.next().getName()); } } }
例子9:演示 “? super E”(接收E类型或者E的父类型)用法。
package cn.itheima.day12; import java.util.Comparator; import java.util.TreeSet; public class GenericDemo08 { public static void main(String[] args) { //TreeSet(Comparator<? super E> comparator) TreeSet<Student1> ts = new TreeSet<Student1>(new CompareByName2()); ts.add(new Student1("s1")); ts.add(new Student1("s2")); ts.add(new Student1("s3")); System.out.println(ts); TreeSet<Worker> ts1 = new TreeSet<Worker>(new CompareByName2()); ts1.add(new Worker("w1")); ts1.add(new Worker("w2")); ts1.add(new Worker("w3")); System.out.println(ts1); } } //? super E//只要能接收集合中的元素对象即可所以Student可以 Person也可以。 class CompareByName2 implements Comparator<Person6>{ @Override public int compare(Person6 p1, Person6 p2) { return p1.getName().compareTo(p2.getName()); } } class Person6{ private String name; Person6(String name) { this.name = name; } public String getName(){ return name; } public String toString(){ return "name:"+name; } } class Worker extends Person6{ Worker(String name) { super(name); } } class Student1 extends Person6{ Student1(String name){ super(name); } }
例子10:演示addAll()的用法.
package cn.itheima.day12; import java.util.ArrayList; import java.util.Iterator; public class GenericDemo09 { public static void main(String[] args) { ArrayList<Person7> al =new ArrayList<Person7>(); al.add(new Person7("p1")); al.add(new Person7("p2")); System.out.println(al); ArrayList<Person7> al1 =new ArrayList<Person7>(); al1.add(new Person7("p3")); al1.add(new Person7("p4")); System.out.println(al1); al.addAll(al1); System.out.println(al); ArrayList<Student3> al2 = new ArrayList<Student3>(); al2.add(new Student3("ss1")); al2.add(new Student3("ss2")); al2.add(new Student3("ss3")); al.addAll(al2); //父类的引用指向子类的对象。 System.out.println(al); Iterator<Person7> it = al.iterator(); while(it.hasNext()){ Person7 p = it.next(); System.out.println(p.getName()); } } } class Person7{ private String name; Person7(String name) { this.name = name; } public String getName(){ return name; } public String toString(){ return "name:"+name; } } class Worker1 extends Person7{ Worker1(String name) { super(name); } } class Student3 extends Person7{ Student3(String name){ super(name); } }
集合框架中的另一个顶层接口Map Map集合特点: 1,Map是一个双列集合,Collection是单列集合。 2,Map一次存一对元素,同时键值对的形式。键和值有对应关系。Collection是一次存一个元素。 3,Map集合必须要保证集合中键的唯一性。 比方:Collection 是单身汉,Map集合存的是一对夫妻。 Map集合中常见的功能: 1,添加。 v put(k,v):将k和v作为元素存储如map集合,当存入了相同的k时,新的值会覆盖原来的值,并返回原来的值。 void putAll(map); 2,删除。 clear();清除。 vremove(k):按照键删除,返回被删除的键对应的值。 3,判断。 booleancontainsKey(Object key) booleancontainsValue(Object value); booleanisEmpty(); 4,获取。 intsize():获取map集合的元素个数。 vget(k): 通过键获取值。还可以作为判断某一个键是否存在的依据。 Collectionvalues():获取map集合中所有的值。 SetkeySet():获取map集合中所有的键。 SetentrySet():获取的是键值的映射的关系。将映射关系封装成对象存入到了Set集合。 Map集合居然没有迭代器。是的。迭代器是Collection集合具备的。Map集合的取出元素的原理:就是将Map集合先转成Set集合,再进行迭代。 Map |--Hashtable:底层是哈希表数据结构。是同步的。不允许null作为键,null作为值。 |--Properties:用于配置文件的定义和操作,使用频率非常高,同时键和值都是字符串。是集合中可以和IO技术相结合的对象,到了IO在学习它的特有和io相关的功能。 |--HashMap:底层也是哈希表数据结构。是不同步的。允许null作为键,null作为值。替代了Hashtable. |--LinkedHashMap:可以保证HashMap集合有序。存入的顺序和取出的顺序一致。 |--TreeMap:可以用来对Map集合中的键进行排序.
例子11.演示Map集合中常见的功能。
package cn.itheima.day13; import java.util.HashMap; public class HashMapDemo { public static void main(String[] args) { //存储学号和姓名(注意:泛型中只能传引用数据类型,不能传基本数据类型) HashMap<Integer, String> hm = new HashMap<Integer, String>(); //添加元素 hm.put(1, "wangwu"); hm.put(2, "lisi"); hm.put(3, "zhangsan"); hm.put(4, "zhaoliou"); System.out.println(hm.size()); System.out.println(hm); //获取 System.out.println(hm.get(2)); //删除 //System.out.println(hm.remove(3)); //清除 //hm.clear(); System.out.println(hm.containsKey(2)); } }
如何获取集合中所有元素呢? 1,获取所有的键,在通过对所有的键进行遍历,在遍历中通过get方法获取每一个键的对应的值。使用 Map集合中 keySet方法:获取Map集合中键的集合。 Set<Integer>keySet = hm.keySet(); //对set集合进行迭代器。 Iterator<Integer>it = keySet.iterator(); while(it.hasNext()){ Integeri = it.next(); Strings = hm.get(i); System.out.println(i+"::"+s); } 2,将map集合中的键值关系取出,并封装成一个键值关系对象。再存储到一个set集合中。键值映射关系封装对象后的数据类型是:Map.Entry。Entry无非就是一个Map接口中的内部静态接口。作为一个键值关系对象的数据类型存在。使用entrySet获取map集合中的所有键值。 Set<Map.Entry<Integer, String>> entrySet = hm.entrySet(); Iterator<Map.Entry<Integer,String>> it1 = entrySet.iterator(); while(it1.hasNext()){ Map.Entry<Integer, String> me = it1.next(); Integer key = me.getKey(); String name = me.getValue(); System.out.println(key+"----"+name); }
例子12:Map集合的两种取出方式实例代码。
package cn.itheima.day13; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; public class HashMapDemo2 { public static void main(String[] args) { //存储学号和姓名(注意:泛型中只能传引用数据类型,不能传基本数据类型) HashMap<Integer, String> hm = new HashMap<Integer, String>(); //添加元素 hm.put(1, "wangwu"); hm.put(2, "lisi"); hm.put(3, "zhangsan"); hm.put(4, "zhaoliou"); hm.put(5, "yangyan"); hm.put(6, "zhouqi"); //获取Map集合中的所有的元素,方法一: Set<Integer> keySet = hm.keySet(); Iterator<Integer> it = keySet.iterator(); while(it.hasNext()){ Integer key = it.next(); String name = hm.get(key); System.out.println(key+"------"+name); } //获取Map集合中的所有的元素,方法二: Set<Map.Entry<Integer, String>> entrySet = hm.entrySet(); Iterator<Map.Entry<Integer, String>> it1 = entrySet.iterator(); while(it1.hasNext()){ Map.Entry<Integer, String> me = it1.next(); Integer key = me.getKey(); String name = me.getValue(); System.out.println(key+"----"+name); } } }<pre><span style="font-family:Microsoft YaHei;">例子13. 每一个学生都有自己的归属地。学生有姓名和年龄。将学生封装成student对象。因为学生对象和归属地有对应关系。所以用map集合存储该关系。键:Student。值:String同姓名同年龄的学生视为同一个人。</span>
package cn.itheima.day13; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; /** 练习:每一个学生都有自己的归属地。 学生有姓名和年龄。 将学生封装成student对象。 因为学生对象和归属地有对应关系。 所以用map集合存储该关系。 键:Student。 值:String 同姓名同年龄的学生视为同一个人。 * @author wl-pc */ public class HashMapTest { public static void main(String[] args) { HashMap<Student, String> hm = new HashMap<Student, String>(); //想要保证有序,即存入的元素和取出的元素的顺序一致。 //这时可以使用一个在hash表中加入链表结构的对象,叫做LinkHashMap. //该对象是hashMap的子类。 hm = new LinkedHashMap<Student, String>(); hm.put(new Student("lisi1", 21), "上海"); hm.put(new Student("lisi6", 26), "河南"); hm.put(new Student("lisi4", 24), "北京"); hm.put(new Student("lisi1", 21), "上海"); hm.put(new Student("lisi7", 27), "广州"); hm.put(new Student("lisi4", 24), "北京"); hm.put(new Student("lisi3", 23), "香港"); Set<Map.Entry<Student, String>> entrySet = hm.entrySet(); Iterator<Map.Entry<Student, String>> it = entrySet.iterator(); while(it.hasNext()){ Map.Entry<Student, String> me = it.next(); Student stu = me.getKey(); String address = me.getValue(); System.out.println(stu.getName()+"::"+stu.getAge()+"----"+address); } } } /** * 要保证学生对象的唯一性。需要建立学生对象自身的判断相同的依据。 而且要根据学生的判断条件来定义依据。 因为是存放了Hash表中,所以要覆盖hashCode方法,和equals方法。 * @author wl-pc */ class Student{ private String name; private int age; Student(String name,int age){ this.name = name; this.age = age; } //覆盖hashCode()方法 @Override public int hashCode() { final int NUM = 23; return name.hashCode() + age*NUM; } //覆盖equals()方法 @Override public boolean equals(Object obj) { if(this==obj) return true; if(!(obj instanceof Student)) return false; Student stu = (Student)obj; //判断同姓名和同年龄的是同一个student return this.name.equals(stu.name) && this.age == stu.age; } public String getName(){ return name; } public int getAge(){ return age; } }
例子14, 利用TreeMap集合对学生的年龄进行排序获取。
package cn.itheima.day13; import java.util.Collection; import java.util.Comparator; import java.util.Iterator; import java.util.Set; import java.util.TreeMap; /** 需求:对学生的年龄进行排序获取。 * @author wl-pc */ public class TreeMapDemo { public static void main(String[] args) { TreeMap<Student1, String> hm = new TreeMap<Student1, String>(new CompareByName()); hm.put(new Student1("lisi2", 22), "上海"); hm.put(new Student1("lisi3", 23), "香港"); hm.put(new Student1("lisi6", 26), "河南"); hm.put(new Student1("lisi7", 27), "广州"); hm.put(new Student1("lisi4", 24), "北京"); Set<Student1> keySet = hm.keySet(); Iterator<Student1> it = keySet.iterator(); while(it.hasNext()){ Student1 stu = it.next(); String address = hm.get(stu); System.out.println(stu.getName()+"::"+stu.getAge()+"---"+address); } //获取所有的学生的归属地 Collection<String> coll = hm.values(); Iterator<String> it1 = coll.iterator(); while(it1.hasNext()){ String address = it1.next(); System.out.println("address::"+address); } } } //定义集合比较器 class CompareByName implements Comparator<Student1>{ @Override public int compare(Student1 s1, Student1 s2) { int temp = s1.getName().compareTo(s2.getName()); return temp==0?new Integer(s1.getAge()).compareTo(new Integer(s2.getAge())):temp; } } /** * 要保证学生对象的唯一性。需要建立学生对象自身的判断相同的依据。 而且要根据学生的判断条件来定义依据。 因为是存放了Hash表中,所以要覆盖hashCode方法,和equals方法。 * @author wl-pc */ class Student1 implements Comparable<Student1>{ private String name; private int age; Student1(String name,int age){ this.name = name; this.age = age; } //复写元素自身比较方法 @Override public int compareTo(Student1 stu) { int num = this.age -stu.age; return num==0?this.name.compareTo(stu.name):num; } //覆盖hashCode()方法 @Override public int hashCode() { final int NUM = 23; return name.hashCode() + age*NUM; } //覆盖equals()方法 @Override public boolean equals(Object obj) { if(this==obj) return true; if(!(obj instanceof Student)) return false; Student1 stu = (Student1)obj; //判断同姓名和同年龄的是同一个student return this.name.equals(stu.name) && this.age == stu.age; } public String getName(){ return name; } public int getAge(){ return age; } }
例子15:."cbxzbvavdvgd"获取字符串中,每一个字母出现的次数。要求结果是:a(1)b(2)c(1)d(2)g(1)v(3)x(1)z(1) 当分析问题时,发现对象间存在映射关系,这时应该第一个想到用map集合存储。分析该练习发现。每一个字母和自己的出现的次数都有对应,而且最终结果,字母都是唯一的。那么这时就想到了map集合。因为从结果中看到打印的字母都有顺序。所以可以使用map集合中treemap集合。 思路: 1,将字符串变成字符数组,因为要操作字符串中的每一个字母。 2,遍历数组,将每一个字母都作为键去map集合获取值。 3,如果获取的值为null。说明该键不存在,就将该键和1存入到集合中。如果该值不为null,说明该键已经存在于map集合,并有对应的值(次数)。那么就将该值取出,并自增后,再将该键和新的值存入到map集合。因为键相同,那么新值会覆盖旧值。 4,遍历结束map集合中就已经具备了每一个字母对应的次数。 5,将map集合中的数据变成字符串打印。
package cn.itheima.day13;
import java.util.Iterator;
import java.util.TreeMap;
/**
* 需求:"cbxzbvavdvgd"获取字符串中,每一个字母出现的次数。
要求结果是:a(1)b(2)c(1)d(2)g(1)v(3)x(1)z(1)
* @author wl-pc
*/
public class MapTest {
public static void main(String[] args) {
String str= "c+b-xz+bv-avdv*gd";
charCount(str);
}
public static void charCount(String str){
TreeMap<Character, Integer> tm = new TreeMap<Character, Integer>();
//将字符串变成字符数组
char[] chs = str.toCharArray();
//遍历数组
//因为要对每一个字母进行计数,可以定义一个变量
int count = 0;
for(int x=0;x<chs.length;x++){
if((chs[x]>='a' && chs[x]<='z' || chs[x]>='A' && chs[x] <='Z')){
//将字母作为键去查map集合,获取对应的值。
Integer i = tm.get(chs[x]);
if(i!=null){
count = i;
}
count++;
tm.put(chs[x], count);
count = 0;
}
}
System.out.println(mapToString(tm));
}
//将map集合变成字符串
private static String mapToString(TreeMap<Character, Integer> tm){
StringBuilder sb = new StringBuilder();
Iterator<Character> it = tm.keySet().iterator();
while(it.hasNext()){
Character key = it.next();
Integer value = tm.get(key);
sb.append(key+"("+value+")");
}
return sb.toString();
}
}
例子35:描述,heima
|--Andriod
|--01 lisi
|--02 wangwu
|--IOS
|--01 zhaoliou
|--02 zhouqi
package cn.itheima.day13;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
/**
* 需求:heima
* Andriod IOS
* Andriod:
* 01 lisi
* 02 wangwu
* IOS:
* 01 zhaoliou
* 02 zhouqi
* @author wl-pc*
*/
public class MapDemo {
public static void main(String[] args) {
//集合中嵌套集合
//heima的集合的键是String类型的,而值是一个HashMap集合类型
HashMap<String,HashMap<String, String> > heima = new HashMap<String,HashMap<String,String>>();
//而andriod和ios都是一个HashMap集合,它们的键是String类型的,而值也是一个String类型的。
HashMap<String, String> andriod = new HashMap<String, String>();
HashMap<String, String> ios = new HashMap<String, String>();
heima.put("heima", andriod);
heima.put("ios", ios);
andriod.put("01","lisi");
andriod.put("02", "wangwu");
ios.put("01", "zhaoliou");
ios.put("02", "zhouqi");
//循环外层大的HashMap集合
Set<String> keySet = heima.keySet();
Iterator<String> it = keySet.iterator();
while(it.hasNext()){
String key = it.next();
HashMap<String, String> hm = heima.get(key);
System.out.println(hm.toString());
show(hm);
}
}
//循环大集合中的小集合
private static void show(HashMap<String, String> hm) {
Set<String> keySet = hm.keySet();
Iterator<String> it1 = keySet.iterator();
while(it1.hasNext()){
String num = it1.next();
String name = hm.get(num);
System.out.println(num+"::"+name);
}
}
}
例子16:将学员的信息封装成Student类,然后一一的获取学生信息。
package cn.itheima.day13;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
/**
* 需求:heima
* Andriod IOS
* Andriod:
* 01 lisi
* 02 wangwu
* IOS:
* 01 zhaoliou
* 02 zhouqi
* @author wl-pc
*/
public class MapDemo2 {
public static void main(String[] args) {
//集合中嵌套集合
//heima的集合的键是String类型的,而值是一个HashMap集合类型
HashMap<String,ArrayList<Student3>> heima = new HashMap<String,ArrayList<Student3>>();
//而andriod和ios都是一个HashMap集合,它们的键是String类型的,而值也是一个String类型的。
ArrayList<Student3> andriod = new ArrayList<Student3>();
ArrayList<Student3> ios = new ArrayList<Student3>();
heima.put("heima", andriod);
heima.put("ios", ios);
andriod.add(new Student3("01", "lisi"));
andriod.add(new Student3("02", "wangwu"));
ios.add(new Student3("01", "zhaoliou"));
ios.add(new Student3("02", "zhouqi"));
//循环外层大的HashMap集合
Set<String> keySet = heima.keySet();
Iterator<String> it = keySet.iterator();
while(it.hasNext()){
String className = it.next();
ArrayList<Student3> hm = heima.get(className);
System.out.println(hm.toString());
show(hm);
}
}
//循环大集合中的小集合
private static void show(ArrayList<Student3> al) {
Iterator<Student3> it1 = al.iterator();
while(it1.hasNext()){
Student3 stu = it1.next();
System.out.println(stu.getNum()+"::"+stu.getName());
}
}
}
class Student3{
private String num;
private String name;
public Student3(String num,String name) {
this.num = num;
this.name = name;
}
public String getNum(){
return num;
}
public String getName(){
return name;
}
}
例子17:有一堆元素。需要唯一不?不需要。选择List集合。需要增删吗?不需要,选择ArrayList。但是想要让元素按指定顺序排序。这时就可以考虑使用集合框架的工具类来完成,看看工具类中是否有方法可以完成搞定。 工具类有两个。 Collections Arrays 这两个工具类的特点:类中的方法都是静态的。不需要创建对象,直接使用类名调用即可。 Collections:是集合对象的工具类。提供了操作集合的工具方法。 Arrays:是数组的工具类,提供了对数组的工具方法。 Collections: List synchronizedList(List):可以将一个不同步的list集合转成一个同步的list集合。 XXX synchroizedXXX(XXX):将非同步的集合变成同步集合的方法。(XXXX可以是Set,还可以是Map) ArrayList和Vertor有什么区别? 一个安全,一个不安全。一个效率高,一个效率低。一般用ArrayList替换了Vertor。 当用多线程访问ArrayList时,怎么解决安全问题? 1.可以自己加锁。 2.集合框架工具类当中的Collections中的一个synchronizedList(list)方法,将非同步集合转成同步集合。 集合面试题最常见: Collection和Collections的区别。
例子17:Collections集合对象的方法演示。
package cn.itheima.day13; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.TreeSet; /** * 需求:有一堆元素,想要让元素按指定顺序排序。 * @author wl-pc */ public class CollectionsDemo { public static void main(String[] args) { //method_1(); //method_2(); //method_3(); //method_4(); //method_5(); method_6(); } //演示其他方法 private static void method_6() { ArrayList<String> al = new ArrayList<String>(); al.add("abcd"); al.add("cba"); al.add("aaaa"); al.add("zz"); al.add("a"); //Collections.fill(al, "AA"); //将集合中的元素替换成指定的元素AA //Collections.reverse(al); //将集合中的元素反转,头尾对调 //Collections.swap(al, 1, 3); //对集合al中的数据元素进行位置置换 //Collections.shuffle(al); //对集合中的元素进行随机的位置置换。 Collections.replaceAll(al, "cba", "heima"); System.out.println(al); } private static void method_5() { ArrayList<String> al = new ArrayList<String>(); al.add("abcd"); al.add("cba"); al.add("aaaa"); al.add("zz"); al.add("cba"); al.add("nba"); //因为二分查找必须是对有序的集合,也就是必须要有角标的集合。 Collections.sort(al); int index = Collections.binarySearch(al, "cba"); System.out.println("index="+index); int index1 = halfSearch(al,"cba"); System.out.println("index1="+index1); } //演示list集合的二分查找的原理 private static int halfSearch(List<String> list, String key) { int max,min,mid; max = list.size()-1; min = 0; while(min<max){ mid = (max+min)>>1; String temp = list.get(mid); int num = key.compareTo(temp); if(num>0) //代表key > temp,这时候小的角标的地方要变化 min = mid+1; else if (num<0) //代表key < temp,这时候大的角标的地方要变化 max = mid-1; else return mid; } return -min-1; } //Collections.max(); //按照自然顺序获取元素其中的最大值 private static void method_4() { ArrayList<String> al = new ArrayList<String>(); al.add("abcd"); al.add("cba"); al.add("aaaa"); al.add("zz"); al.add("cba"); al.add("nba"); //String max = Collections.max(al); //按照自然顺序获取其中的最大值 //按照指定的比较器来获取其中的最大值 String max= Collections.max(al, new CompareByLen()); System.out.println("max="+max); } //自定义的得到元素的最大值的方法分析,泛型限定 /*public static <T extends Object & Comparable<? super T>> T getMax(Collection<? extends T> al) { }*/ //反转元素:Collections.reverseOrder(); private static void method_3() { TreeSet<String> ts = new TreeSet<String>(Collections.reverseOrder()); ts.add("cba"); ts.add("aaaa"); ts.add("zz"); ts.add("a"); ts.add("nba"); System.out.println(ts); } //演示:Collections.reverseOrder(Comparator com);逆转顺序的比较器 private static void method_2() { ArrayList<String> al = new ArrayList<String>(); al.add("abcd"); al.add("cba"); al.add("aaaa"); al.add("zz"); al.add("cba"); al.add("nba"); //在排序时,传递一个可以逆转顺序的比较器即可,reverseOrder(); //Collections.reverseOrder(al,Collections.reverseOrder()); //按照长度排序,想要让长度从长到短排序,只要让比较器逆转即可。 Collections.sort(al,Collections.reverseOrder(new CompareByLen())); Iterator<String> it = al.iterator(); while(it.hasNext()){ System.out.println(it.next()); } } //演示:Collections.sort();集合排序 private static void method_1() { ArrayList<String> al = new ArrayList<String>(); al.add("abcd"); al.add("cba"); al.add("aaaa"); al.add("zz"); al.add("cba"); al.add("nba"); //Collections.sort();可以对List集合(只能对List集合)进行按照元素的自然排序给元素排序 //Collections.sort(al); Collections.sort(al,new CompareByLen()); Iterator<String> it = al.iterator(); while(it.hasNext()){ System.out.println(it.next()); } } } //定义集合比较器实现按照元素的长度进行比较 class CompareByLen implements Comparator<String>{ @Override public int compare(String s1, String s2) { int num = s1.length()-s2.length(); return num==0?s1.compareTo(s2):num; } }
例子18:Arrays集合的数组变集合的方法实例演示。
package cn.itheima.day13; import java.util.Arrays; import java.util.List; /** * Arrays工具类用来操作数组。 * @author wl-pc */ public class ArraysDemo { public static void main(String[] args) { //Arrays中有一个重要的方法,将数组变为集合 String[] arr = {"heima","csdn","itcast"}; List<String> list = java.util.Arrays.asList(arr); //将数组变为集合 //数组变为集合后,就可以使用集合的方法来操作数组,而不用自己在定义指针进行数组的操作。 //但是有些方法是不可以使用的,例如:只要改变集合的长度的方 //法都是不可以使用的,因为数组是固定长度的。 boolean b = list.contains("heima"); System.out.println("b="+b); //list.add("wangling"); //数组长度是不可以改变的(也就是不能增删),所以有不支持UnsupportedOperationException异常出现 //System.out.println(list); /*int[] arr1 = {2,4,3,8,7,9}; int index = Arrays.binarySearch(arr1,4); //折半查找 System.out.println("index="+index);*/ mothod(); } private static void mothod() { int[] arr2 ={3,4,6}; //当数组中的元素时引用数据类型时,变为集合后,就将数组中的元素作为集合中的元素存在。 //当数组中的元素时基本数据类型时,变为集合后,会将数组变成集合中的元素。 List<int[]> list1 = Arrays.asList(arr2); System.out.println(list1); System.out.println(Arrays.toString(arr2)); //将数组直接变成字符串。 } }
例子19:集合变数组的实例演示。
package cn.itheima.day13; import java.util.ArrayList; import java.util.Arrays; /** * 集合变为数组。 * 用的是Collection接口中的一个toArray()方法完成的。 * @author wl-pc * 将集合变成数组有什么用呢? 其实是限定了对元素的增删操作。 */ public class CollectionToArray { public static void main(String[] args) { ArrayList<String> al = new ArrayList<String>(); al.add("heima"); al.add("csdn"); al.add("itcast"); String[] arr = al.toArray(new String[al.size()]); //将集合变为数组 /** * 给toArray方法传递指定类型的数组。 长度该怎么定义呢? 当指定的长度小于集合的长度, 该方法内部会自动创建一个该类型的新数组长度和集合长度一致 。用于存储集合中的元素。 如果指定的数组长度大于集合的长度, 那么该方法就不会创建新数组。而是使用传递进来的数组, 存储完集合的元素后,其他的未存储的位置为null。 所以在定义数组是,最好定义长度和集合长度相同的数组。这样就不用创建新数组了。 而且也不会出现空位为null 。 将集合变成数组有什么用呢? 其实是限定了对元素的增删操作。 */ System.out.println(Arrays.toString(arr)); } }
例子20.JDK1.5后的新特性,增强for循环。
package cn.itheima.day13; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; /** * JDK1.5之后的新特性。 * @author wl-pc * Collection就有一个父接口Iterator * 该接口的出现封装了iterator方法,并提供了一个增强for循环的方法。 * 格式: * for( 元素类型 变量 : 数组或者Collection集合){ * } * 增强for循环与普通循环有什么不同? * 增强for循环不能定义变量,而普通for循环可以。 * 增强for循环使用时必须要有被比遍历的目标。 * 而且只能遍历数组和Collection集合,简化了迭代。 * 传统for循环更为普遍。 * 建议遍历数组使用传统的for循环,如果只为遍历可以用增强for循环。 */ public class ForeachDemo { public static void main(String[] args) { ArrayList<String> al = new ArrayList<String>(); al.add("abcd"); al.add("cba"); al.add("aaaa"); al.add("zz"); al.add("a"); //注意:用foreach循环只能是遍历元素的内容,不能对元素做改变的操作 //如果要对遍历的集合做出操作,就用迭代器,itertor可以操作元素 for (String list : al) { System.out.println(list); } //迭代器具备在迭代过程中对元素操作,比如:remove() //通过ListItertor具备在迭代过程中队元素进行增删改查。 /*Iterator<String> it = al.iterator(); while(it.hasNext()){ System.out.println(it.next()); }*/ int[] arr1 = {3,2,4,5,7}; for (int i : arr1) { System.out.println("i="+i); } //要对Map集合进行遍历,可以使用foreach循环吗?答:不可以直接使用,可以间接使用 //因为Map集合不是Collection集合的子接口,也就没有itertor迭代器, //所以不能直接使用,只能把Map集合变为Set集合,在使用foreach循环。 HashMap<String, String> hm = new HashMap<String, String>(); hm.put("01", "lisi"); hm.put("02", "zhangsan"); hm.put("03", "wangwu"); hm.put("04", "zhaoliou"); Set<Map.Entry<String, String>> entrySet = hm.entrySet(); Iterator<Map.Entry<String, String>> it = entrySet.iterator(); /*while(it.hasNext()){ Map.Entry<String, String> key = it.next(); String num =key.getKey(); String name = key.getValue(); System.out.println(num+"::"+name); }*/ for (Map.Entry<String, String> entry : entrySet) { System.out.println(entry.getKey()+"::"+entry.getValue()); } } }
当一个函数操作的参数类型一致,但是参数个数不一致的时候,可以定义成该类型的数组参数。这样,使用者,就可以把元素封装成一个数组。在进行传递即可。我们发现,还要new一个数组,也麻烦。所以jdk1.5以后出现了新特性。 就是可变参数,在指定数据类型的后面加上三个点 ,其实就是一个数组类型的参数, 定义数组和定义一个可变参数,有哪里不一样了呢? 以前定义一个int[] 类型的参数,调用必须要定义好一个数组,才往里传递。而现在定义一个int... 类型的参数,调用者,直接往该函数中传递元素即可。在运行时,自动会将这些实际参数封装到一个该类型的数组当中。也就是调用省去了封装数组的步骤,变简单了。这个升级也是简化书写。 注意:如果函数上有多个参数,可变参数一定要定义在参数列表最后面。否则编译失败。
例子21. Java的新特性:可变参数实例演示。
package cn.itheima.day13; /** * Java的新特性:可变参数。 * @author wl-pc */ public class ParamterDemo { public static void main(String[] args) { //show(5); //show1(5,6); //int[] arr = {2,5,6,8,3}; //show3(arr); show4(2,4,6,7,8); } //public static void method(int... arr,int a) //这个定义是错误的。 //定义一个可变参数的的方法 private static void show4(int... arr) { //可变参数,其实arr就是一个数组类型的参数 for (int i : arr) { System.out.println(i); } } //定义一个int型的数组来存放变量 /*public static void show3(int[] arr){ //System.out.println(Arrays.toString(arr)); for (int i : arr) { System.out.println(i); } }*/ /*private static void show(int x) { System.out.println("x="+x); } private static void show1(int x,int y) { System.out.println("x="+x+",y="+y); }*/ }
例子22:JDK.1.5 特性:静态导入实例演示。
package cn.itheima.day13;
import java.util.ArrayList;
//import java.util.Arrays;
//import java.util.Collections;
import java.util.*;
import static java.util.Collections.*; //导入了一个类中的所有静态成员。
import static java.util.Arrays.*;
/**
* JDK.1.5 特性:静态导入。
* @author wl-pc
* 注意:当方法重复时,必须制定所属的类名。
*/
public class StaticImportDemo {
public static void main(String[] args) {
ArrayList<Integer> al = new ArrayList<Integer>();
al.add(1);
al.add(3);
al.add(5);
al.add(7);
al.add(9);
//Collections.sort(al);
sort(al);
System.out.println(al);
//int index = Collections.binarySearch(al, 5);
int index = binarySearch(al, 5);
System.out.println("index="+index);
int[] arr = {3,5,6};
sort(arr);
System.out.println(Arrays.toString(arr));
}
}