泛型,通配符,Map

版权声明:本文为博主原创文章,转载请标明出处。https://blog.csdn.net/kXYOnA63Ag9zqtXx0/article/details/82954503 https://blog.csdn.net/forever428/article/details/83240617

泛型

通过<数据类型>接收一种数据类型,在编译的时候会使用这种类型检测集合中的元素,如果不是<>中规定的类型,就不允许添加到集合当中(编译不通过).

作用:

  1. 使用了泛型不再需要进行强制类型转换,容错处理,向下转型.------简化代码
  2. 将运行阶段的问题提前到了编译阶段检查,提高了代码的安全性和编程的效率.
  • 泛型的擦除:泛型只是在编辑阶段检查元素的类型,一到运行阶段泛型就消失了.
  • 泛型的使用场景:
    1. 类上
    2. 接口上
    3. 方法上
public static void main(String[] args) {
		//当list指定了具体的String泛型之后,里面的元素只能是String
		ArrayList<String> list = new ArrayList<>();
		list.add("java1");
		list.add("java2");
		list.add("java5");
		list.add("java7");
		//list.add(3);
		
		System.out.println(list);
		
		//获取迭代器
		//当集合的泛型确定之后,让迭代器的泛型与之一致即可
		Iterator<String> iterator = list.iterator();
		
		//使用泛型前
		//遍历
//		while (iterator.hasNext()) {
//			Object object = (Object) iterator.next();
//			//容错处理
//			if (!(object instanceof String)) {
//				throw new ClassCastException("数据转化异常");
//			}
//			//向下转型
//			String string = (String)object;
//			
//			System.out.println(string.length());
//		}
		
		//使用泛型后
		while (iterator.hasNext()) {
			String string = (String) iterator.next();
			System.out.println(string.length());
		}
	}

泛型在类上的使用


public class Demo3 {
	public static void main(String[] args) {
		Student student = new Student();
		Computer computer = new Computer("苹果");
		Phone phone = new Phone("苹果");
		
		//将工具交给学生
		student.setObj(computer);//多态
		
		//使用泛型前
		//获取工具
//		Object object =  student.getObj();//多态
//		
//		//向下转型
//		Computer computer2 = (Computer)object;//computer2 = object = new Computer("苹果")
//		Phone phone2 = (Phone)object;//phone2 = object = new Computer("苹果")  错误
		
		//使用泛型后
		//这里通过使用泛型:保证了Student1类的使用灵活性而且可以简化代码,提高代码的效率
		Student1<Computer> student1 = new Student1<>();
		student1.setObj(computer);
		
		Computer computer3 =  student1.getObj();
	}
}

//使用泛型前
class Student{
	private Object obj;

	public Object getObj() {
		return obj;
	}

	public void setObj(Object obj) {
		this.obj = obj;
	}
}

//使用泛型后
/*
 * E代表任意一种数据类型,但是<>中不一定是E,可以是任意字符.
 * 在类的后面加<>就是在给类使用泛型.
 * 在类上确定的泛型可以在成员上直接使用
 */
class Student1<E>{
	private E obj;

	public E getObj() {
		return obj;
	}

	public void setObj(E obj) {
		this.obj = obj;
	}
}

class Tool{
	String name;
	public Tool() {
	}
	public Tool(String name) {
		this.name = name;
	}
}

class Phone extends Tool{
	public Phone(String name) {
		super(name);
	}
}

class Computer extends Tool{
	public Computer(String name) {
		super(name);
	}
}

泛型在方法上的使用

  1. 方法上的泛型与类上的泛型一致
  2. 方法上独立使用泛型
    • 注意:泛型在使用之前一定要先进行声明
    • 声明的方式:在方法的最前面加<符号>
    • 作用:让方法内与方法的泛型保持一致
  3. 静态方法上使用泛型
    • 静态方法上无法使用类上的泛型,类上的泛型必须通过创建对象才能使用.
    • 静态方法的调用不需要对象,所以静态方法如果想使用泛型,必须自己单独使用.
import java.util.ArrayList;

public class Demo4 {
	public static void main(String[] args) {
		Teacher<String> teacher = new Teacher<>();
		//1.方法上的泛型与类上的泛型一致
		teacher.play("haha");
		//2.方法上独立使用泛型
		teacher.show(2);
		//3.静态方法上使用泛型
		Teacher.run(5);
	}
}

class Teacher<E>{
	//1.方法上的泛型与类上的泛型一致
	public E play(E e){
		return e;
	}
	//2.方法上独立使用泛型
	/*
	 * 注意:泛型在使用之前一定要先进行声明
	 * 声明的方式:在方法的最前面加<符号>
	 * 作用:让方法内与方法的泛型保持一致
	 */
	public <F> void show(F f){
		ArrayList<F> list = null;
	}
	//3.静态方法上使用泛型
	/*
	 * 静态方法上无法使用类上的泛型,类上的泛型必须通过创建对象才能使用.
	 * 静态方法的调用不需要对象,所以静态方法如果想使用泛型,必须自己单独使用.
	 */
	public static<W> void run(W w){
		
	} 
}

接口上使用泛型

子类使用泛型的情况

第一种:接口有泛型,子类没有遵守对应的泛型
注意:必须给接口指定一个具体的泛型类型

第二种:接口有泛型,子类与接口一致,遵守了对应的泛型
类上的泛型确定了,接口上的泛型就确定了,重写方法上的泛型也就确定了

public class Demo5 {
	public static void main(String[] args) {
		Dog dog = new Dog();
		dog.play("haha");
		
		Cat<String> cat = new Cat<>();
	}
}

//接口上使用泛型
interface Inter<E>{
	public  void play(E e);
}

//子类使用泛型的情况
/*
 * 第一种:接口有泛型,子类没有遵守对应的泛型
 * 注意:必须给接口指定一个具体的泛型类型
 */
class Dog implements Inter<String>{
	//重写的方法的泛型与接口一致
	public void play(String e) {
		// TODO Auto-generated method stub
		
	}
	//自定义的方法可以使用接口的泛型,也可以自定义泛型.
	public <F>  void show(F f) {
		
	}
}

/*
 * 第二种:接口有泛型,子类与接口一致,遵守了对应的泛型
 * 类上的泛型确定了,接口上的泛型就确定了,重写方法上的泛型也就确定了
 */

class Cat<E> implements Inter<E>{
	public void play(E e) {
		// TODO Auto-generated method stub
		
	}
}

? : 通配符,表示一种或几种数据类型

  • 限制上限:<? extends E>:限制的是整个<>中可以取的泛型类型是E类及E类的子类

  • 限制下限:<? super E>:限制的是整个<>中可以取的泛型类型是E类及E类的父类

  • 注意点:在取类型的时候,类型之间一定有继承关系.

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

/*
 * 这里讲的是限制上限:<? extends E>
 */
public class Demo6 {
	public static void main(String[] args) {
		//
		ArrayList<Student2> list1 = new ArrayList<>();
		list1.add(new Student2("bingbing", 1));
		//可以传参:因为Student2是Person1的子类,可以实现遍历
		bianli(list1);
		
		ArrayList<Teacher8> list2 = new ArrayList<>();
		list2.add(new Teacher8("bingbing", 1));
		//可以传参:因为Teacher1是Person1的子类,可以实现遍历
		bianli(list2);
		
		ArrayList<Person1> list3 = new ArrayList<>();
		list3.add(new Person1("bingbing", 1));
		//可以传参
		bianli(list3);
		
		ArrayList<Object> list4 = new ArrayList<>();
		list4.add(new Object());
		//可以传参:因为Object是Person1的父类,不可以实现遍历
		//bianli(list4);
	}
	
	
	public static void bianli(Collection<? extends Person1>  e){
		System.out.println("遍历了");
	}
}

class Person1{
	String name;
	int age;
	public Person1() {
		super();
		// TODO Auto-generated constructor stub
	}
	public Person1(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	@Override
	public String toString() {
		return "Person1 [name=" + name + ", age=" + age + "]";
	}
}

class Teacher8 extends Person1{
	public  Teacher8(String name, int age) {
		super(name, age);
	}
}

class Student2 extends Person1{
	public  Student2(String name, int age) {
		super(name, age);
	}
}

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

public class Demo7 {
	public static void main(String[] args) {
		//限制下限 <? super E>
		//TreeSet(Comparator<? super E> comparator) :这里的E跟TreeSet后面的泛型类型一致,所以现在E应该表示的Student3
		
		//创建Student3类的比较器对象
		ComWithStu comWithStu = new ComWithStu();
		//创建Teacher1类的比较器对象
		ComWithTea comWithTea = new ComWithTea();
		//创建Person1类的比较器对象
		ComWithPerson comWithPerson = new ComWithPerson();
		//创建GoodStudent类的比较器对象
		ComWithGood comWithGood = new ComWithGood();
		
		TreeSet<Student3> set = new TreeSet<>(comWithStu);//因为这里限制的是Student3及他的父类
		//TreeSet<Student3> set = new TreeSet<>(comWithTea);//不可以使用,因为Teacher2类与Student3类没有关系
		//TreeSet<Student3> set = new TreeSet<>(comWithPerson);//可以  ,因为Person2类是Student3类的父类
		//TreeSet<Student3> set = new TreeSet<>(comWithGood);//不可以,因为GoodStudent类是Student3类的子类
		set.add(new Student3("bingbing"));
		set.add(new Student3("bingbing1"));
		set.add(new Student3("bingbing2"));
	}
}
//创建Student3类的比较器
class ComWithStu implements Comparator<Student3>{

	public int compare(Student3 o1, Student3 o2) {
		
		return o1.name.compareTo(o2.name);
	}
}
//创建Teacher2类的比较器
class ComWithTea implements Comparator<Teacher2>{
	public int compare(Teacher2 o1, Teacher2 o2) {
		
		return 0;
	}
}
//创建Person2类的比较器
class ComWithPerson implements Comparator<Person2>{
	public int compare(Person2 o1, Person2 o2) {
        return 0;
	}
}

//创建GoodStudent类的比较器
class ComWithGood implements Comparator<GoodStudent>{
	public int compare(GoodStudent o1, GoodStudent o2) {
		return 0;
	}
}

class Person2{
	 String name;

	public Person2(String name) {
		super();
		this.name = name;
	}

	public String toString() {
		return "Person2 [name=" + name + "]";
	}
}

class Teacher2 extends Person2{
	public Teacher2(String name) {
		super(name);
	}
}
class Student3 extends Person2{
	public Student3(String name) {
		super(name);
	}
}

class GoodStudent extends Student3{
	public GoodStudent(String name) {
		super(name);
	}
}

Map:

  • 比较Map和Collection

    • Collection:是集合,是接口,是直接存值
    • Map:是集合,是接口,存储的是键值对.一个元素就是一个键(key)值(value)对.键必须是唯一的,值可以相同.
  • Map需要设置两个泛型,一个是key的,一个是value的.

  • 设置key是,最好使用一个常量作为key,比如String,因为他本事是固定不变的,从map的底层实现来看,进行存取的效率会更高.

image

Map接口的方法

  1. 增加
    • V put(K key,V value) 增加一个键值对
    • void putAll(Map<? extends K,? extends V> map) 增加多个
  2. 删除
    • V remove(Object key) 根据key删除元素
    • void clear() 删除全部
  3. 获取
    • V get(Object key) 根据key查找元素
    • int size() 获取键值对的个数
    • Set keySet() 遍历方法一
    • Set<Map.Entry<K,V>> entrySet() 遍历方法二
  4. 常用的判断
    • boolean isEmpty()
    • boolean containsKey(K key) 是否包含当前的key
    • boolean containsValue(V value) 是否包含当前的value
public class Demo8 {
    public static void main(String[] args) {
    	/*
    	 * Map需要设置两个泛型,一个是key的,一个是value的.
    	 * 设置key是,最好使用一个常量作为key,比如String,因为他本事是固定不变的,从map的底层实现来看,进行存取的效率会更高.
    	 */
    	Map<String, String> map = new HashMap<String, String>();
		//介绍Map接口的方法
		//1.增加
		//V put(K key,V value)  增加一个键值对
    	map.put("01", "java");
    	map.put("02", "html");
    	map.put("03", "python");
    	map.put("04", "BigData");
    	/*
    	 * 由于map中的key是唯一的,所以当给同一个key赋两次值,后面的值会将前面的值覆盖.
    	 * 对于返回值:如果当前的key之前没有对应值,返回null.如果已经有值了,会将原来被替换的值返回.
    	 */
    	System.out.println(map.put("01", "iOS"));
    	System.out.println(map);
		//void putAll(Map<? extends K,? extends V> map)  增加多个
		//2.删除
		//V remove(Object key)  根据key删除元素
//   	System.out.println(map.remove("01"));
		//void clear()  删除全部
//    	map.clear();
//    	System.out.println(map);
		//3.获取
		//V get(Object key)  根据key查找元素
    	System.out.println(map.get("04"));
		//int size()  获取键值对的个数
    	System.out.println(map.size());
		//Set<K> keySet()   遍历方法一
		//Set<Map.Entry<K,V>> entrySet() 遍历方法二
		//4.常用的判断
		//boolean isEmpty()
    	System.out.println(map.isEmpty());//false
		//boolean containsKey(K key) 是否包含当前的key
    	System.out.println(map.containsKey("01"));//true
		//boolean containsValue(V value) 是否包含当前的value
    	System.out.println(map.containsValue("java"));//false
	}
}

Set keySet() 遍历方法一

  • 通过这个方法得到所有的key,存放在set中,利用set的迭代器遍历得到每一个key,再利用key得到值

Set<Map.Entry<K,V>> entrySet() 遍历方法二

  • 通过这个方法得到所有的entry(键值对),存放在set中,利用set的迭代器遍历得到每一个entry,再调用entry的方法得到key和value

了解:

  • 为什么要将Entry映射关系接口放入Map接口中?

    Entry是Map内的一个静态接口.因为有了集合中的键值对才有映射关系.而当前的映射关系又是对集合内部的描述.所以…

public class Demo9 {
	public static void main(String[] args) {
		Map<String, String> map = new HashMap<>();
    	map.put("01", "java");
    	map.put("02", "html");
    	map.put("03", "python");
    	map.put("04", "BigData");
    	
    	System.out.println(map);
    	
    	//Set<K> keySet()   遍历方法一
    	//得到set
    	Set<String> set1 = map.keySet();
    	//得到迭代器
    	Iterator<String> iterator1 = set1.iterator();
    	//遍历得到所有的key,通过key获取value
    	while (iterator1.hasNext()) {
			String key = (String) iterator1.next();
			System.out.println("key:"+key+"   value:"+map.get(key));
		}
    	
    	//Set<Map.Entry<K,V>> entrySet() 遍历方法二
    	//得到set
    	Set<Map.Entry<String,String>> set2 = map.entrySet();
    	//得到迭代器
    	Iterator<Map.Entry<String,String>> iterator2 = set2.iterator();
    	//遍历
    	while (iterator2.hasNext()) {
			Map.Entry<String, String> entry = (Map.Entry<String, String>) iterator2.next();
			//注意:这里可以通过setValue对map的值进行更改,但是不建议在这里改.
			//entry.setValue("ok");
			System.out.println("key1:"+entry.getKey()+"   value1:"+entry.getValue());
		}
	}
}

HashMap

对他的去重就是对key进行去重,方法与操作HashSet一致—通过给key重写hashCode和equals方法

TreeMap:对他的去重和排序就是对key进行去重和排序,与TreeSet一致

  1. 系统排序 让key实现Comparable接口,重写compareTo方法
  2. 人工排序 创建比较器类,实现Comparetor接口,重写compare方法.然后直接作用于TreeMap即可.
public class Lianxi1 {

	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
		 
		LinkedList linkedList1 = new LinkedList();
		Line line = new Line();
		while(true)		{		
		System.out.println("请选择添加还是删除字符,添加按1.删除按2.退出按0 ");
		String choose = scanner.next();		
		if (choose.equals("2")) {//判断用户选择的功能
			if (linkedList1.size()==0) {//先判断集合此时有没有元素,如果没有要先添加元素
				System.out.println("现在集合为空,请先添加字符");
				continue;
			}else {	
				linkedList1 = line.deleteCharater(linkedList1);//从集合的开头删除元素
			}
		} 
		if(choose.equals("1")) {//判断用户选择的功能
			System.out.println("请输入要添加的字符");
			String string = scanner.next();
			linkedList1 = line.addCharater(linkedList1, string);//从集合的后方添加元素
			
		}
		if(choose.equals("0")) {//判断用户选择的功能
			System.exit(0);
			
		}
		else if(!(choose.equals("1")||choose.equals("2"))) {//判断用户选择的功能,如果不是需要的数字,就提醒输入不正确
			System.out.println("请输入正确的数字");
		}
		
		}		
	}
}

class Line{
	
	
	public LinkedList addCharater(LinkedList linkedList, String string) {		
		linkedList.addLast(string);	
		System.out.println(linkedList);
		return linkedList;
	}
public LinkedList deleteCharater(LinkedList linkedList) {
		
		linkedList.pollFirst();	
		System.out.println(linkedList);
		return linkedList;
	}
}
import java.util.TreeSet;
import java.util.Comparator;
public class Lianxi2 {
	/*步骤:1.创建比较器类实现Comparetor接口,重写compare方法
	    2.创建比较器对象,并指定给存储元素的TreeSet*/
	public static void main(String[] arrgs){
             Person3 person =new  Person3 ();   
		TreeSet set = new TreeSet<>(person);//2.将Person3的对象存入TreeSet,按照年龄和比较
		set.add(new Person3("bingbing",16));
		set.add(new Person3("bingbing",16));
		set.add(new Person3("bingbing1",15));
		set.add(new Person3("bingbing",15));
		System.out.println(set);
	}

}

TreeMap的注意点

  1. 什么类型的数据可以作为TreeMap的key?

    答:实现了Comparable接口,或者使用了比较实现了Comparetor接口)的元素.常见的:String nteger,Double
    不行的类型:ArrayList,数组

  2. 作为key的类型,与他的内部有没有关系?

    答:可以.元素能不能充当key,与他内部无关.只看他有没有实现Comparable接口.

import java.util.ArrayList;
import java.util.HashMap;
import java.util.TreeMap;

public class Demo2 {
	public static void main(String[] args) {
		//验证:ArrayList,数组不能充当Map的key,因为他们本身都没有实现Comparable接口
//		TreeMap<ArrayList<Cat>, String> map = new TreeMap<>();
//		map.put(new ArrayList<>(), "01");
//		System.out.println(map);
		
//		TreeMap<Cat[], String> map = new TreeMap<>();
//		map.put(new Cat[]{new Cat(),new Cat()}, "01");
//		System.out.println(map);
		
		// *2.作为key的类型,与他的内部有没有关系? 
		//*答:可以.元素能不能充当key,与他内部无关.只看他有没有实现Comparable接口.
		TreeMap<Cat, String> map = new TreeMap<>();
		map.put(new Cat(), "01");
		System.out.println(map);
	}
}

class Cat implements Comparable<Cat>{
	String name;
	int age;
	Object obj;
	@Override
	public int compareTo(Cat o) {
		// TODO Auto-generated method stub
		return 0;
	}
}

猜你喜欢

转载自blog.csdn.net/forever428/article/details/83240617