------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
集合(2)
主要内容:《 泛型、泛型的方法、接口、通配符、增强for、可变参数、静态导入、嵌套、Set集合 》
前言:为什么使用泛型
1.对于集合类,就其本身而言,是可以添加任何的引用类型。因为它的add()方法的形参是Object类型;
2.但,这样在我们取出时,会造成很大的麻烦,我们总要向下转型为我们存储的类型;一旦判断失误,就会导致
转换失败,会抛出异常。
3.我们在平时使用集合类时,更多的情况是"只存储一种类型"。
4.所以这时,Java为我们提供了一种方式,可以在定义集合时,就规定好这个集合内能够存储什么类型的数据,
一旦定义之后,它的add()方法就只能接受那种类型的数据。这种方式:泛型
5.泛型的语法:
ArrayList<String> list = new ArrayList<String>();
或者:
ArrayList<String> list = new ArrayList<>();(比较常用)
或者:
ArrayList<String> list = new ArrayList();
6.注意:泛型:只在"编译期"存在。一旦生成class文件后,泛型信息就没有了。
说明,只是用在"编译"时,进行"类型检查"。
1.泛型的格式与定义
格式:
ArrayList<String> strList = new ArrayList<String>();
或者:
ArrayList<String> strList = new ArrayList<>();(建议使用)
或者:
ArrayList<String> strList = new ArrayList();
2.泛型的定义:
1).泛型类:
class MyArrayList<T>{
public void show(T t){
}
}
说明:
A.<T>就是一个泛型的定义,在类名的后面;
B.可以使用大写或小写字母,或单词都可以;<T>,<t>,<a,b>
C.可以定义多个泛型,多个泛型之间用,逗号隔开;<T,B,C>
2.泛型的方法
class MyArrayList{
//此方法能够保证:传入的是什么类型,返回的就是什么类型;
public <T> T show(T t){
}
}
3.泛型接口
interface IA<T>{
void show(T t);
}
当子类实现时:
1.可以继续使用泛型:
class SubA<T> implements IA<T>{
void show(T t){
}
}
2.可以丢弃泛型:
class SubA implements IA{
void show(Object obj){
}
}
3.可以定义为某个具体类型:
class SubA implements IA<String>{
void show(String str){
}
}
4.泛型通配符:
1.<?>:
1).能够指向什么类型的对象?可以指向具有任何具体类型泛型的集合对象;
ArrayList<?> list1 = new ArrayList<>();//OK的
ArrayList<?> list2 = new ArrayList<String>();//OK的
ArrayList<?> list3 = new ArrayList<Integer>();//OK的
2).可以添加什么类型的引用?
list1:不能添加任何类型;
list2:不能添加任何类型;
list3:不能添加任何类型;
3).取出时,用什么接收?
接收全部使用Object接收;
特点:不能向里面存东西,但可以从里面取东西。这种声明一般用于方法的返回值;
2.<? extends E>:
1).能够指向什么类型的对象?可以指向具有E或者E的子类类型泛型的集合对象;
ArrayList<? extends B> list4 = new ArrayList<A>();//NO
ArrayList<? extends B> list5 = new ArrayList<B>();//OK
ArrayList<? extends B> list6 = new ArrayList<C>();//OK
public <? extends B> show(){
ArrayList<B> list = new ArrayList<B>();
ArrayList<C> list2 = new ArrayList<C>();
//return list;
return list2;
}
取出时,可以使用E或E的父类类型接收;
特点:不能向里面存东西,只能从里面取东西。这种声明一般用于方法的返回值;表示,这个方法返回的集合,里面存的可能是E类型,或者E的某个子类类型;
3.<? super E>:
1).能够指向什么类型的对象?可以指向具有E或者E的"父类"类型泛型的集合对象;
2).可以添加什么类型的引用?可以添加E或E的子类对象的引用;
3).取出时,用什么接收?取出时只能用Object接收;
特点:存入的时候是任何E或E的子类对象的引用。取出时,仍然使用Object接收。所以这种声明一般用于方法的形参
1 class A{ 2 } 3 class B extends A{ 4 } 5 class C extends B{ 6 } 7 class D extends B{ 8 9 } 10 public class Demo { 11 public static void main(String[] args) { 12 //一.**************通配符:<?>*************************** 13 //1.能够指向什么类型的对象? 14 ArrayList<?> list1 = new ArrayList<>(); 15 ArrayList<?> list2 = new ArrayList<String>(); 16 ArrayList<?> list3 = new ArrayList<Integer>(); 17 //2.可以添加什么类型的引用? 18 //向list1中添加元素: 19 // list1.add("aa");//NO 20 // list1.add(10);//NO 21 // list1.add(new Object());//NO 22 23 //向list2中添加元素: 24 // list2.add("aaa");//NO 25 // list2.add(10);//NO 26 // list2.add(new Object());//NO 27 28 //向list3中添加元素: 29 // list3.add("aaa");//NO 30 // list3.add(10);//NO 31 // list3.add(new Object());//NO 32 //3.取出时,用什么接收? 33 Object obj1 = list1.get(0);//OK的 34 Object obj2 = list2.get(0);//OK的 35 Object obj3 = list3.get(0);//OK的 36 //二.**************通配符:<? extends E>*************************** 37 //1.能够指向什么类型的对象? 38 // ArrayList<? extends B> list4 = new ArrayList<A>();//NO 39 ArrayList<? extends B> list5 = new ArrayList<B>();//OK 40 ArrayList<? extends B> list6 = new ArrayList<C>();//OK 41 42 //2.可以添加什么类型的引用? 43 // list5.add(new A());//NO 44 // list5.add(new B());//NO 45 // list5.add(new C());//NO 46 47 // list6.add(new A());//NO 48 // list6.add(new B());//NO 49 // list6.add(new C());//NO 50 51 //3.取出时,用什么接收: 52 B b1 = list5.get(0); 53 B b2 = list6.get(0); 54 55 //三.**************通配符:<? super E>*************************** 56 //1.能够指向什么类型的对象? 57 ArrayList<? super B> list7 = new ArrayList<A>();//OK 58 ArrayList<? super B> list8 = new ArrayList<B>();//OK 59 // ArrayList<? super B> list9 = new ArrayList<C>();//NO 60 //2.可以添加什么类型的引用? 61 // list7.add(new A());//No 62 list7.add(new B());//OK 63 list7.add(new C());//OK 64 65 // list8.add(new A());//NO 66 list8.add(new B());//OK 67 list8.add(new C());//OK 68 69 //3.取出时,用什么接收? 70 Object obj4 = list7.get(0); 71 Object obj5 = list8.get(0); 72 73 74 75 } 76 }
5.增强for
增强for循环:
1.语法格式:
ArrayList<String> list = new ArrayList<>();
for(集合里面存储的数据类型 变量名 : 集合/数组变量名){
}
for(String str : list){
}
2.增强for循环的特点:
1).没有循环变量;一般用于不需要循环变量的循环操作;如果在循环中需要循环变量,还是使用普通for循环;
2).可以遍历集合,数组;
3).增强for循环底层就是使用的"迭代器"。因为仍然会产生并发修改异常;
1 public class Demo { 2 public static void main(String[] args) { 3 //1.定义一个存储String类型的集合 4 ArrayList<String> strList = new ArrayList<>(); 5 //2.填充数据 6 strList.add("aaa"); 7 strList.add("bbb"); 8 strList.add("ccc"); 9 //3.使用增强for遍历 10 strList = null; 11 for(String s : strList){ 12 System.out.println(s); 13 /* if(s.equals("bbb")){ 14 strList.add("ddd");//仍然能够产生"并发修改异常"。 15 }*/ 16 } 17 for(int i = 0;i < strList.size() ; i++){ 18 String s = strList.get(i); 19 System.out.println(s); 20 } 21 22 //变量数组 23 int[] intArray = {1,32,4,243,25,43,5}; 24 for(int n : intArray){ 25 System.out.println(n); 26 } 27 } 28 }
6.可变参数
1.定义一个方法,这个方法可以接收0 -- 无限多的某类型实参。这种形参就是:可变参数;
2.格式:
public int getMax(int ... nums){
}
注意:数据类型:可以是任何的Java类型(基本数据类型和引用数据类型);
3.调用getMax()方法时,可以传递:0 -- 无限多;
getMax();//不传参数可以。
getMax(1432,432,43,25,432,432,43,25,325,2,432,4532);//传多个参数也可以;
4.注意:
1).可变参数可以同其它形参共存,但可变参数一定要位于参数列表的末尾;
5.可变参数,在方法内部,是按照"数组"的方式工作的。
通过反编译后,我们也看出,可变形参也被编译为数组的形式;
1 public class Demo { 2 public static void main(String[] args) { 3 //1.求两个数的最大值; 4 int a = 10; 5 int b = 20; 6 int max = MyMath.getMax(a,b); 7 System.out.println("max = " + max); 8 /* 9 int[] array = {1,432,4,3245,32,4}; 10 max = MyMath.getMax(array); 11 System.out.println("max = " + max); 12 */ 13 14 max = MyMath.getMax(14,3,432,432,5,435,32,432,5,435,2,432,45,324); 15 System.out.println("max = " + max); 16 17 } 18 19 }
7.静态导入
1.直接导入到某个类的静态方法名;
import static java.lang.Math.abs;
2.可以使用通配符
import static java.lang.Math.*;
8.集合的嵌套
1.一个集合,可以存储任何类型的对象,包括"集合"。
2.所以:一个集合可以存储另一个集合,这就叫:集合的嵌套;
例子:一个班有3名同学,有3个姓名。另一个班有5名同学,有5个姓名;
要求:将两个班合并为一个集合打印:
1.定义一个集合,存储第一个班的3名学员;
2.定义一个集合,存储第二个班的5名学员;
3.定义一个大集合,将前面的两个小集合存入;
4.遍历大集合
1 public class Demo { 2 public static void main(String[] args) { 3 //1.第一个班的集合 4 ArrayList<String> class1List = new ArrayList<>(); 5 class1List.add("马云"); 6 class1List.add("马化腾"); 7 class1List.add("雷军"); 8 9 //2.第二班的集合 10 ArrayList<String> class2List = new ArrayList<>(); 11 class2List.add("成龙"); 12 class2List.add("甄子丹"); 13 class2List.add("李连杰"); 14 class2List.add("吴京"); 15 class2List.add("洪金宝"); 16 17 //3.实例化一个大集合 18 ArrayList<ArrayList<String>> list = new ArrayList<>(); 19 list.add(class1List); 20 list.add(class2List); 21 22 //4.打印大集合 23 for(ArrayList<String> classList : list){ 24 for(String name : classList){ 25 System.out.println(name); 26 } 27 } 28 } 29 }
9.Set集合
Collection
|--List(接口):
|--Set(接口):它是Collection的子接口,所以,它继承了所有Collection中的
一些基本方法,我们可以找一个子类,直接测试;
特点:
1.无序(取出时跟存入时的顺序不一致)
2.不存储重复值;
|--HashSet(类):内部使用"哈希表"实现;
Set集合存储字符串
1 public class Demo { 2 public static void main(String[] args) { 3 //1.定义一个HashSet对象; 4 Set<String> strSet = new HashSet<>(); 5 //2.填充集合:Collection的add(Object o); 6 strSet.add("aaa"); 7 strSet.add("bbb"); 8 strSet.add("ccc"); 9 strSet.add("ddd"); 10 11 strSet.add("ccc");//不存储重复值 12 strSet.add("ddd");//不存储重复值 13 strSet.add("eee"); 14 15 String s1 = new String("ccc"); 16 strSet.add(s1);//不存储重复值; 17 18 19 //3.遍历集合: 20 //方式一:toArray(); 21 Object[] objArray = strSet.toArray(); 22 for(Object o : objArray){ 23 System.out.println(o);//取出时跟存入的顺序不一致; 24 } 25 System.out.println("************************************"); 26 //方式二:iterator() 27 Iterator it = strSet.iterator(); 28 while(it.hasNext()){ 29 System.out.println(it.next()); 30 } 31 //方式三:因为增强for是基于迭代器的,所以,凡是能用迭代器遍历的,使用增强for都可以 32 System.out.println("************************************"); 33 for(String s : strSet){ 34 System.out.println(s); 35 } 36 37 } 38 }
10.HashSet保证元素的唯一性:
1.由于HashSet内部使用了"哈希表",所以它会先产生"哈希值";
2.通过查看源码,我们知道,判断重复元素的方式:
1.先判断:hashCode是否相同;
2.如果hashCode相同,再判断equals;
3.所以,我们使用HashSet存入自定义对象,如果想使HashSet存储不同的元素,排除掉--不同对象,但内部属性完全相同的对象,
我们就要在我们的自定义类中重写:hashCode()和equals()
1 public class Demo { 2 public static void main(String[] args) { 3 //1.实例化一个Set 4 Set<Student> stuSet = new HashSet<>(); 5 //2.填充数据 6 stuSet.add(new Student("张三",20)); 7 stuSet.add(new Student("李四",22)); 8 stuSet.add(new Student("王五",24)); 9 Student stu1 = new Student("周六",26); 10 Student stu2 = new Student("周六",26); 11 /* 12 System.out.println(stu1.hashCode()); 13 System.out.println(stu2.hashCode());*/ 14 15 stuSet.add(stu1); 16 17 stuSet.add(stu2); 18 19 //3.遍历 20 for(Student stu : stuSet){ 21 System.out.println(stu.getName() + "," + stu.getAge()); 22 } 23 } 24 }
1 package cn.itcast.demo02_Set集合保证元素的唯一性; 2 3 public class Student { 4 private String name; 5 private int age; 6 public Student(String name, int age) { 7 super(); 8 this.name = name; 9 this.age = age; 10 } 11 public Student() { 12 super(); 13 } 14 public String getName() { 15 return name; 16 } 17 public void setName(String name) { 18 this.name = name; 19 } 20 public int getAge() { 21 return age; 22 } 23 public void setAge(int age) { 24 this.age = age; 25 } 26 /* 27 @Override 28 public int hashCode() { 29 //一般情况下,是将内部的成员进行一个int值的累加: 30 //对于基本数据类型,尽量转换为int值; 31 //对于引用类型,调用它的hashCode()方法; 32 33 int code1 = this.age; 34 int code2 = this.name.hashCode();//调用String类内部的hashCode()方法,此方法是重写Object 35 36 return code1 + code2; 37 } 38 39 public boolean equals(Object obj){ 40 Student stu = (Student)obj; 41 return this.name.equals(stu.name) && this.age == stu.age; 42 } 43 */ 44 @Override 45 public int hashCode() { 46 final int prime = 31; 47 int result = 1; 48 result = prime * result + age; 49 result = prime * result + ((name == null) ? 0 : name.hashCode()); 50 return result; 51 } 52 @Override 53 public boolean equals(Object obj) { 54 if (this == obj) 55 return true; 56 if (obj == null) 57 return false; 58 if (getClass() != obj.getClass()) 59 return false; 60 Student other = (Student) obj; 61 if (age != other.age) 62 return false; 63 if (name == null) { 64 if (other.name != null) 65 return false; 66 } else if (!name.equals(other.name)) 67 return false; 68 return true; 69 } 70 71 72 73 } 74
11.LinkedHashSet类
Collection
|--List
|--Set
|--HashSet
|--LinkedHashSet类:
特点:
1.内部由链表和哈希表实现;
2.链表:保证顺序;
哈希表:保证唯一;
1 public class Demo { 2 public static void main(String[] args) { 3 //1.实例化一个LinkedHashSet 4 Set<Student> set = new LinkedHashSet<>(); 5 //2.填充数据 6 set.add(new Student("刘德华",20,'男',80.0)); 7 set.add(new Student("张学友",22,'男',83.0)); 8 set.add(new Student("黎明",24,'男',88.0)); 9 set.add(new Student("郭富城",26,'男',90.0)); 10 11 set.add(new Student("郭富城",26,'男',90.0)); 12 //3.遍历 13 for(Student stu : set){ 14 System.out.println(stu.getName() + "," + stu.getAge() + "," + stu.getSex() + "," + stu.getScore()); 15 } 16 17 } 18 }
1 package cn.itcast.demo04_LinkedHashSet类; 2 3 public class Student { 4 private String name; 5 private int age; 6 private char sex; 7 private double score; 8 public Student(String name, int age, char sex, double score) { 9 super(); 10 this.name = name; 11 this.age = age; 12 this.sex = sex; 13 this.score = score; 14 } 15 public String getName() { 16 return name; 17 } 18 public void setName(String name) { 19 this.name = name; 20 } 21 public int getAge() { 22 return age; 23 } 24 public void setAge(int age) { 25 this.age = age; 26 } 27 public char getSex() { 28 return sex; 29 } 30 public void setSex(char sex) { 31 this.sex = sex; 32 } 33 public double getScore() { 34 return score; 35 } 36 public void setScore(double score) { 37 this.score = score; 38 } 39 @Override 40 public int hashCode() { 41 final int prime = 31; 42 int result = 1; 43 result = prime * result + age; 44 result = prime * result + ((name == null) ? 0 : name.hashCode()); 45 long temp; 46 temp = Double.doubleToLongBits(score); 47 result = prime * result + (int) (temp ^ (temp >>> 32)); 48 result = prime * result + sex; 49 return result; 50 } 51 @Override 52 public boolean equals(Object obj) { 53 if (this == obj) 54 return true; 55 if (obj == null) 56 return false; 57 if (getClass() != obj.getClass()) 58 return false; 59 Student other = (Student) obj; 60 if (age != other.age) 61 return false; 62 if (name == null) { 63 if (other.name != null) 64 return false; 65 } else if (!name.equals(other.name)) 66 return false; 67 if (Double.doubleToLongBits(score) != Double 68 .doubleToLongBits(other.score)) 69 return false; 70 if (sex != other.sex) 71 return false; 72 return true; 73 } 74 75 76 }
12.Map概述
Map集合,就是基于"键,值"对的映射关系
1."键"不能重复:
2.每个"键"映射一个"值";
Map和Collection的不同:
1.Map是双列的(键、值对)。Collection都是单列的。
2.Map的键唯一,Collection的子体系Set是唯一的:HashSet(唯一)内部使用-->HashMap。所以HashMap对于重复的"键值"的判断,跟HashSet的判断方式 是一样的;
3.Map集合的数据结构值针对键有效,跟值无关;Collection集合的数据结构是针对元素有效
13.Map接口的基本方法
Map(接口):无序的。键、值对存储
|--HashMap(类):
Map的基本方法:
V put(Object key,Object value):添加元素。key做键,value做值
V remove(Object key):删除key所对应的值,key也会一起删除;
void clear():清空集合
boolean containsKey(Object key):判断key在集合中是否存在
boolean containsValue(Object value):判断value在集合中是否存在;
boolean isEmpty():判断集合是否为空
int size():返回集合内的元素的数量;
1 public class Demo { 2 public static void main(String[] args) { 3 Map<String,String> map = new HashMap<>(); 4 //V put(Object key,Object value):填充集合 5 map.put("it001", "刘德华"); 6 map.put("it002", "张学友"); 7 map.put("it003", "周润发"); 8 map.put("it004", "鞠萍"); 9 map.put("it001", "刘亦菲");//当存储重复的键时,会使用新值,替换原值; 10 11 //遍历集合 12 System.out.println(map); 13 //V remove(Object key)移除鞠萍 14 map.remove("it004"); 15 System.out.println("删除掉鞠萍后:" + map); 16 //3.void clear() 17 /* map.clear(); 18 System.out.println("清空集合后:" + map);*/ 19 //4.boolean containsKey(Object key) 20 System.out.println("键it001在集合中是否存在:" + map.containsKey("it001")); 21 System.out.println("键it009在集合中是否存在:" + map.containsKey("it009")); 22 23 //5. boolean containsValue(Object value) 24 System.out.println("值\"刘德华\"在集合中是否存在:" + map.containsValue("刘德华")); 25 System.out.println("值\"刘亦菲\"在集合中是否存在:" + map.containsValue("刘亦菲")); 26 27 //6.boolean isEmpty() 28 /*System.out.println("集合是否为空:" + map.isEmpty()); 29 map.clear(); 30 System.out.println("清空集合后,集合是否为空:" + map.isEmpty());*/ 31 32 //7.int size(); 33 System.out.println("集合大小:" + map.size()); 34 System.out.println("最后打印集合:" + map); 35 } 36 }
14.Map接口的获取方法
Map接口的获取的方法:
V get(Object key):通过一个key获取一个value;
Set<K> keySet():获取所有的key,以一个Set返回;
Collection<V> values():获取所有的value,以一个Collection
1 public class Demo { 2 public static void main(String[] args) { 3 Map<String,String> map = new HashMap<>(); 4 //V put(Object key,Object value):填充集合 5 map.put("it001", "刘德华"); 6 map.put("it002", "张学友"); 7 map.put("it003", "周润发"); 8 map.put("it004", "鞠萍"); 9 10 //1.V get(Object key) 11 String value = map.get("it001"); 12 System.out.println("value = " + value); 13 System.out.println("**************************"); 14 //2.Set<K> keySet() 15 Set<String> keySet = map.keySet(); 16 for(String key : keySet){ 17 System.out.println(key); 18 } 19 System.out.println("**************************"); 20 //3.Collection<V> values() 21 Collection<String> values = map.values(); 22 for(String v : values){ 23 System.out.println(v); 24 } 25 System.out.println("**************************"); 26 //遍历Map 27 Set<String> keys = map.keySet(); 28 for(String k : keys){ 29 String v = map.get(k); 30 System.out.println("键:" + k + " 值:" + v); 31 } 32 } 33 }
15.获取"键值对"的对象
1 public class Demo { 2 public static void main(String[] args) { 3 Map<String,String> map = new HashMap<>(); 4 //V put(Object key,Object value):填充集合 5 map.put("it001", "刘德华"); 6 map.put("it002", "张学友"); 7 map.put("it003", "周润发"); 8 map.put("it004", "鞠萍"); 9 10 //获取"键值对"的对象 11 Set<Map.Entry<String, String>> enSet = map.entrySet(); 12 for(Map.Entry<String, String> en : enSet){ 13 String key = en.getKey(); 14 String value = en.getValue(); 15 System.out.println(key + "--" + value); 16 17 } 18 } 19 }
16.LinkedHashMap类
1.内部由链表和哈希表实现;
2.链表:保证了有序;
哈希表:保证了唯一;
注意:Map子类的数据结构,都是应用在"键"上的。
1 public class Demo { 2 public static void main(String[] args) { 3 //1.实例化一个Map 4 Map<String,String> map = new LinkedHashMap<>(); 5 //2.填充集合 6 map.put("it001", "刘德华"); 7 map.put("it002", "张学友"); 8 map.put("it003", "周润发"); 9 map.put("it004", "郭富城"); 10 map.put("it001", "刘亦菲");//覆盖原it001的value 11 12 //3.遍历 13 Set<String> keySet = map.keySet(); 14 for(String key : keySet){ 15 System.out.println(key + "---" + map.get(key)); 16 } 17 } 18 }