泛型、MAP集合
/** * */ package com.qfedu.Day18.HomeWork; public abstract class Animal { private int tui; public int getTui() { return tui; } public void setTui(int tui) { this.tui = tui; } public Animal(int tui) { super(); this.tui = tui; } public Animal() { super(); // TODO Auto-generated constructor stub } } /** * */ package com.qfedu.Day18.HomeWork; public class Cat extends Animal { /** * @param tui */ public Cat(int tui) { super(tui); // TODO Auto-generated constructor stub } /** * */ public Cat() { // TODO Auto-generated constructor stub } } /** * */ package com.qfedu.Day18.HomeWork; public class Fish extends Animal{ //子类是无法继承父类的构造方法的 //子类必须提供自己的构造方法 public Fish() { //调用父类的无参构造方法 super(); } public Fish(int tui) { super(tui); // 调用类父类的有参构造方法 } } /** * */ package com.qfedu.Day18.HomeWork; public class QQ extends Animal { /** * @param tui */ public QQ(int tui) { super(tui); // TODO Auto-generated constructor stub } /** * */ public QQ() { // TODO Auto-generated constructor stub } } /** * */ package com.qfedu.Day18.HomeWork; /** * Description: 创建自定义异常<br/> * Copyright (c) , 2018, JK <br/> * This program is protected by copyright laws. <br/> * Program Name:TUIException.java <br/> * * @author 千锋智哥 * @version : 1.0 * 继承Exception --> 编译时异常 --> 在书写代码调用方法或其他的属性时就会出现异常信息,必须处理 * try...catch...finally throws * 继承RuntimeException -->运行时异常 在程序运行以后,才会出现的异常 出现异常之后,可以捕捉也继续抛出 */ public class TUIException extends Exception{ /** * 区分JdK版本号使用 --> 对象流 */ private static final long serialVersionUID = -2974291582387079751L; public TUIException() { super(); // TODO Auto-generated constructor stub } public TUIException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); // TODO Auto-generated constructor stub } public TUIException(String message, Throwable cause) { super(message, cause); // TODO Auto-generated constructor stub } public TUIException(String message) { super(message); // TODO Auto-generated constructor stub } public TUIException(Throwable cause) { super(cause); // TODO Auto-generated constructor stub } } /** * */ package com.qfedu.Day18.HomeWork; public class AnimalFactory { private AnimalFactory() {} public static Animal getAnimal(int tui,String AnimalName) { switch (AnimalName) { case "猫": return new Cat(tui); case "鱼": return new Fish(tui); case "企鹅": return new QQ(tui); default: throw new RuntimeException("没有这种动物"); } } } /** * */ package com.qfedu.Day18.HomeWork; import java.util.HashSet; import java.util.Scanner; import java.util.Set; public class AnimalManagerSystem { public static void main(String[] args) { Scanner input = new Scanner(System.in); Set set = new HashSet(); while(true) { System.out.println("请输入您要添加的动物:"); String name = input.next(); System.out.println("请输入退的个数:"); int tui = input.nextInt(); //通过工厂就能的到对象 try { Animal animal = AnimalFactory.getAnimal(tui, name); addAnimal(animal); set.add(animal); System.out.println("谢谢添加"); break; }catch(TUIException | RuntimeException e) { System.out.println(e.getMessage()); } } } /** * 就是一个检查动物退是否正确的一个方法 * @param a 父类Animal * @throws TUIException 个数不对 */ public static void addAnimal(Animal a)throws TUIException { if(a instanceof Cat) { Cat c = (Cat)a; if(c.getTui() != 4) { throw new TUIException("这个退不对"); } }else if(a instanceof Fish) { Fish f = (Fish)a; if(f.getTui() != 0) { throw new TUIException("这个鱼是不可能有退的,若有拿来看!!!!"); } }else { QQ qq = (QQ)a; if(qq.getTui() != 2) { throw new TUIException("这个退不对"); } } System.out.println("添加成功"); } }
回顾:
学习了Set集合
Set集合的特点就是:无序(无序不等于随机),排重
ps:打印是打印 存储是存储
打印只是展示存储的数值数据
存储是底层存放的方式
实现类:
HashSet:使用Hash表的形式来存数据, 无序 ,排重
当前类的子类:
LinkedHashSet:链表和Hash表的双重实现,即可以排重也可以记录存储信息本身没有特殊方法,使用可以参考Set集合中的方法
TreeSet:二叉树形式存储-->使用红黑二叉树
排重 排序(默认是升序) 使用TreeSet需要注意:存储的数据必须具备可比性(相同数据类型才行)
自定义类进行排序如何使用:
实现两个接口
Comparator 自定义比较器
Comparable 系统比较器
两者接口返回的 使用 正数,负数,零来判断这些升序,降序,不变
两个接口实现的方法时不同的
TreeSet排序建议使用哪个Comparable接口 对应TreeSet默认无参构造方法
Comparator可以使用在一些自己定义排序的方式
例如: Collections.sort Arrays.sort 也能对应TreeSet的有参构造方法
/** * */ package com.qfedu.Day18.HomeWork; public class Worker implements Comparable{ private int age; private int workAge; private int money; public Worker(int age, int workAge, int money) { super(); this.age = age; this.workAge = workAge; this.money = money; } public Worker() { super(); // TODO Auto-generated constructor stub } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public int getWorkAge() { return workAge; } public void setWorkAge(int workAge) { this.workAge = workAge; } public int getMoney() { return money; } public void setMoney(int money) { this.money = money; } /* (non-Javadoc) * @see java.lang.Comparable#compareTo(java.lang.Object) */ @Override public int compareTo(Object o) { //没有泛型之前 Worker other = (Worker)o; int Sage = this.age - other.age; int SworkAge = other.workAge - this.workAge; int Smoney = other.money - this.money; int sum = Sage == 0 ? SworkAge == 0 ? Smoney : SworkAge : Sage; //return sum; return this.age - other.age == 0 ?other.workAge - this.workAge == 0 ? other.money - this.money : other.workAge-this.workAge : this.age-other.age; } /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { return "年龄:"+age+","+"工龄:"+workAge+","+"薪水:"+money; } } /** * */ package com.qfedu.Day18.HomeWork; import java.util.TreeSet; public class Test { public static void main(String[] args) { TreeSet set = new TreeSet(); set.add(new Worker(18, 2, 3000)); set.add(new Worker(18, 5, 3100)); set.add(new Worker(18, 2, 3700)); set.add(new Worker(19, 2, 3200)); set.add(new Worker(30, 10,1500)); System.out.println(set); } }
泛型(genericity):
为什么要使用泛型?
/** * */ package com.qfedu.Day18.Genericity.Tong; import java.util.ArrayList; import java.util.List; public class Demo { public static void main(String[] args) { List<Integer> list = new ArrayList<>(); list.add(123); //list.add("123"); //手动擦除 -->严禁使用 List list1 = list; list1.add("1111"); System.out.println(list); } } /** * */ package com.qfedu.Day18.Genericity.Tong; import java.util.ArrayList; import java.util.List; public class Test { public static void main(String[] args) { //千万不能这样使用 ? 是通配符 错误 // List<?> list = new ArrayList<>(); // list.add("1"); //盖住变量名 去除等号右边 ,剩下的就是数据类型 List<Integer> list1 = new ArrayList<>(); doWork(list1); List<String> list2 =new ArrayList<>(); doWork(list2); } //若集合使用了泛型,此时在方法中作为参数是,也要指定泛型是什么 // public static void doWork(List<Integer> array) { // // } //1.通配符的第一个作用就是作为方法的参数的数据类型而存在 //此时,此方法可以接受任何泛型的集合,传入到方法中 public static void doWork(List<?> list) { //list.add(1); System.out.println(list); } }
案例1:
集合可以存储任何数据类型,必然集合中需要使用Object类型接受
1.若需要使用计算或是当前对象的特有属性和方法强制类型转换(向下转型)
2.向集合中存储元素,无法限制存储元素的数据类型
我们可以使用泛型来约束集合中存储的数据类型,并且使用了泛型后就不需要强制类型转换了(向下转型)
但是这样一来会出现一个问题,不能再存储任何数据类型,只能存储限制的数据类型对应的值
案例2:
设计一个点(Ponit),来封装坐标,要求坐标的数据类型可以支持String,int,double,float,
class Point{
private String x;
private String y;
}
class Point{
private int x;
private int y;
}
....
原则DRY 不要重复自己
可以通过创建对象时在决定Point的数据是什么,这样一来是不是就可以不用创建多个Point而只要建立一个类即可
class Point{
privare T x;
private T y;
}
new Point();
就可以使用泛型
什么是泛型:
1.泛型的含义就是代表任何数据类型,但是本身没有任何含义,就是一个站位符
2.在不能明确数据类型式,我们即可以使用这种占位符的形式来替代数据类型
特点:
通过<数据类型>接收一种引用数据类型,在编译程序时该类型会对[集合、类、接口、方法]中传入的数据进行类型匹配
如果不是当前类型,编译就不会通过,从运行时的问题提升到了编译时
泛型是用在编译时期的,编译完成之后产生.class文件后泛型就不会存在了-->泛型擦除
泛型在定义阶段没有任何意义,只是占位符问题
实际使用中参数的传递或类型的定义,此时当前泛型才会有真正的意义
泛型的分类
泛型应用在类上
泛型中定义的字本身没任何意义,就是占位
看Class包
/**
*
*/
package com.qfedu.Day18.Genericity.Class;
/**
* Point类可以接受String,double,float,int,long 作为参数的数据类型
*/
public class Point<T> {//泛型类 -->类中定义的泛型可以在类体中使用 -->通用性
//在没有给ArrayList指定泛型时,可以接受任何数据类型
//此时定义在ArrayList上的<E> 相当于什么 --> Object
//可以作为属性的数据类型而存在 ,当前数据类型是不能确定了 只是使用了一个字母T作为占位使用
private T x;
private T y;
public Point() {
}
public Point(T x, T y) {//类中方法可以作为数据类型
this.x = x;
this.y = y;
}
public T getX() {
return x;
}
public void setX(T x) {
this.x = x;
}
public T getY() {
return y;
}
public void setY(T y) {
this.y = y;
}
public void show(T z) {
System.out.println(z);
}
}
/**
*
*/
package com.qfedu.Day18.Genericity.Class;
public class Test {
//如何使用泛型类
public static void main(String[] args) {
//不推荐
// Point p1 = new Point();
// //默认是泛型类,在创建对象时不指定泛型类型也可以使用但是,不指定就是Object
// p1.show(10);
//如何创建对象
Point<String> p1 = new Point<>();
p1.setX("1");
//T 相当于 Object
Point<Integer> p2 = new Point<>();
p2.setX(1);
Point<Boolean> p3 = new Point<>();
p3.setX(true);
}
}
/** * */ package com.qfedu.Day18.Genericity.Method; public class MethodClass<T> { //当前方法使用泛型类型就和类是一致的了 public void show(T t) { } //类所定义的泛型不能再静态方法中使用,提供自己的泛型方法 // public static void display(T t) { // System.out.println(t); // } //泛型方法的定义 //什么时候能确定这个泛型的数据类型-->调用方法时传递参数,就会决定方法的泛型的数据数据类型 public<E> void dispaly(E e) { System.out.println(e); } public static<K> void showInfos(K k) { } }
泛型应用在方法上
1.泛型类中的泛型只能适用于非静态方法,如果需要给静态方法添加泛型,此时就需要定义泛型方法了
2.方法想使用自己的泛型类型,并不想使用其他方式提供的泛型,此时就需要使用泛型方法了
看Method包
泛型应用在接口上
看Inteface包
/** * */ package com.qfedu.Day18.Genericity.Inteface; //和类是类似的使用方式 public interface InterFaceDemo<T> { //1.8之前 String NAME = "1"; //public static final //抽象方法 全局静态常量 void show(T t); // public abstract //1.8之后 //定义实现方法 //default 修饰 static 修饰 --> 都是公有public //default可以重写 static 不行 //default可以通过接口对象调用 static接口名调用 } /** * */ package com.qfedu.Day18.Genericity.Inteface; //1.确定泛型接口中泛型的数据类型,使用类实现接口并制定泛型类型 public class Person implements InterFaceDemo<String>{ /* (non-Javadoc) * @see com.qfedu.Day18.Genericity.Inteface.InterFaceDemo#show(java.lang.Object) */ @Override public void show(String t) { // TODO Auto-generated method stub } } /** * */ package com.qfedu.Day18.Genericity.Inteface; //2.泛型类实现泛型接口,此时可以通过泛型类型中的泛型来占位泛型接口中的泛型 // 创建当前泛型类对象的时,此时就可以制定泛型的数据类型 public class Student<M> implements InterFaceDemo<M>{ /* (non-Javadoc) * @see com.qfedu.Day18.Genericity.Inteface.InterFaceDemo#show(java.lang.Object) */ @Override public void show(M t) { // TODO Auto-generated method stub } } /** * */ package com.qfedu.Day18.Genericity.Inteface; public class Test { public static void main(String[] args) { //3.以匿名内部类的形式来完成泛型接口的创建,同时指定泛型类型 new InterFaceDemo<String>() { @Override public void show(String t) { // TODO Auto-generated method stub } }; } }
泛型通配符 ?
通配符不能单独使用,仅限于在使用参数定义数据类型时使用
? 在不知道是什么数据类型时,也可以使用? 来代替
泛型的限定:
<? extends 类>
此时只能传入当前类的子类或当前类
<? super 类>
此时只能传入当前类或父类类型
ps:
泛型擦除:
1.泛型在编译过后会被自动消除
2.当把带有泛型的集合赋值给不带有泛型的集合时,此时泛型被擦除(手动擦除)
Map集合:(非常重要)
Map集合是一种映射集合,map集合集合中存在着一种对应关系 key- value 键值对 -->(JSON数据格式)
要求:
1.key的值必须是唯一的,不能重复
2.value的可以重复必备唯一
key必须唯一,但是Value可以不唯一 一个key只能对应一个value 同一个value可以对应多个key
key这边想成是一个Set集合
value这边想成是一个List集合
HashMap集合常用方法
API
看HashMap包
/** * */ package com.qfedu.Day18.HashMap; public class Card { //cardNo:银行卡号,startDate:开户日期, money private String cardNo; private String startDate; private String money; public Card() { super(); // TODO Auto-generated constructor stub } public Card(String cardNo, String startDate, String money) { super(); this.cardNo = cardNo; this.startDate = startDate; this.money = money; } public String getCardNo() { return cardNo; } public void setCardNo(String cardNo) { this.cardNo = cardNo; } public String getStartDate() { return startDate; } public void setStartDate(String startDate) { this.startDate = startDate; } public String getMoney() { return money; } public void setMoney(String money) { this.money = money; } /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { return "卡号:"+cardNo+"开户日期:"+startDate+"金额:"+money; } } /** * */ package com.qfedu.Day18.HashMap; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Scanner; import java.util.Set; public class HashMapDemo { public static void main(String[] args) { //1.创建Map集合对象 Map<String,String> map = new HashMap<>(); //2.向map集合对象中存储map集合中的元素 Map<String,String> map1 = new HashMap<>(map); //常用方法: //1.向集合中添加元素 /* * 这个方法是有返回值的,返回的是 value的数据类型 */ map.put("key1", "value1"); map.put("key2", "value2"); map.put("key3", "value3"); map.put("key4", "value4"); map.put("key5", "value5"); map.put("key6", "value6"); System.out.println(map); //2.向集合中添加集合(添加集合中的元素) map1.putAll(map); System.out.println(map1); //3.清空集合,集合还在 map1.clear(); System.out.println(map1); //4.判断集合中是否存在指定的key值 //true证明存在 false 不存在 boolean res = map.containsKey("key7"); System.out.println(res); //5.判断集合中是否存在指定的value值 //true证明存在 false 不存在 boolean result = map.containsValue("value7"); System.out.println(result); //6通过key获取value值 ,若key不存在返回null String value = map.get("key4"); System.out.println(value); //7.判断集合是否是空,说明集合存在,没有元素 System.out.println(map1.isEmpty()); //8.key相当于存储到了set集合中 排重 保持唯一 //获取所有的key值并存储到set集合中 Set<String> set = map.keySet(); //此时所有的key是不是都在set集合集合中了 //是不是通过操作set集合就能获取或改变map集合对应的键值对 for(String keys : set) { if(keys.equals("key6")) { //不仅是向集合中添加元素,修改对应的键值对 map.put(keys, "vlaue10"); } } System.out.println(map); //9.获取Map集合中所有Value的值,存储到Collection集合中 Collection<String> c = map.values(); for(String value1 : c) { System.out.println(value1); } //10.获取所有键值对的总和(个数) System.out.println(map.size()); //11.通过key删除对应的键值对 ,返回值是对应的value //删除成功会获取对应的value 失败则返回null String value2 = map.remove("key9"); System.out.println(value2); //12. /* * Set<Map.Entry<K,V>> entrySet() 返回此映射所包含的映射关系的 Set 视图。 通过此方法可以的到一个set集合 需要给set集合添加泛型 这个方法的类型是一个Map.Entry Map.Entry自己也有泛型 这个泛型类型需要和map集合中存储的 key和value的数据类型一致 * */ Set<Map.Entry<String,String>> mapSet = map.entrySet(); System.out.println(mapSet); for(Map.Entry<String, String> entry :mapSet) { //取出key 和 value System.out.println(entry.getKey()+" ---> "+ entry.getValue()); } //若存在两个相同的key值最后一次添加的键值对会替代第一次添加键值对 map1.put("key1", "value1"); map1.put("key1", "value2"); System.out.println(map1); map1.put("key1", "value1"); map1.put("key2", "value1"); System.out.println(map1); /* * 需求: 一个人对应多张银行卡 Card: cardNo:银行卡号,startDate:开户日期, money:余额 请设计一个程序,输入一个人的名称,得到这个人的所有银行卡信息。 键值对 一个对多 value是什么? 分析如下: 1.通过第一句话已知得到一个信息就是需要一个描述Card的方式 在java中描述只能有类来完成所以 应该创建一个Card类 2.Card的后面提供了一些信息 包括卡号 开户日期 余额 这些明显的属于是对类的信息描述,那信息描述应该是类的属性 3.第二句话中隐藏着一些信息,首先既然是输入一个人名 那么必然要用到Scanner 人名是String类型所以是字符串 再者人名是属于对人的一种描述属性 所以应该推断而出 可以创建一个人类 属性是人名 综上所述这个参数即可以用String也可以使用自己创建的类来完成 4.第二句话还有一个信息就是一个人有多张卡的问题 这里应该很明确卡不能是单纯的一张?那么如何来表示多张卡呢?--> 集合 选用那个集合呢? 一个人可以在同一个银行办多张卡 所以在不考虑其他特殊情况下应该使用 List 5.最后将这些信息汇总,即可以通过人名获取对应的卡,那么就需要一个稳定的获取模式 键值对 key-value形式 即 key 是人名 value 是list集合 集合中存在多张卡 */ System.out.println("请输入一个人名:"); Scanner input = new Scanner(System.in); String name = input.next(); List<Card> list = new ArrayList<>(); list.add(new Card("88888888", "1900-01-01", "1")); list.add(new Card("77777777", "1292-02-02", "10000")); list.add(new Card("66666666", "2018-08-22", "1000000000000000000000000")); //非常常见的集合中存储了另外一个集合 Map<String,List<Card>> hmap = new HashMap<>(); hmap.put("王健林", list); System.out.println("稍等正在查询......."); if(hmap.containsKey(name)) { // List<Card> listmap = hmap.get(name); // for(Card c1 : listmap) { // System.out.println(c1); // } System.out.println(hmap.get(name)); }else { System.out.println("不好意思没有这个人"); } } }
LinkedHashMap是HashMap的子类没有特殊方法使用方式参考HashMap即可
TreeMap二叉数 --> 自带排序 这个排序时key排序 ,需要进行排序的数据存储到Key
key是什么集合 Set --> TreeSet
Hashtable和HashMap都是Map的实现类
Hashtable是线程安全的,效率低 - properties 资源文件 --> 加载
HashMap是线程不安全,效率高
总结:
Collection:
List:有序存储,允许存重复值并且有下标 提供了迭代器,for循环等方式遍历集合
主要应用 ArrayList
Set:无序存储,不允许重复,没有下标,提供了迭代器和for循环等遍历方式遍历集合
主要应用 HashSet
TreeSet --> 自带排序功能的集合
必须实现Comparable接口
若自定义类需要进行排序,建议实现Comparable接口
实现Comparator接口也可以
Map:无序 是一种映射关系,键值对 key-value ,key必须唯一,value可以不唯一
并没有提供Iterator,但是提供了另外一种方式 Map.Entry --> entrySet
主要应用 HashMap
Collections工具类:
为集合提供方法可以方便操作
/** * */ package com.qfedu.Day18.Collections; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Vector; public class CollectionsDemo { public static void main(String[] args) { List<Integer> list = new ArrayList<>(); //向集中一次性添加多个元素 /* * 第一个参数需要是一个Collection这边的集合 * 第二个参数是一个可变参数(可以什么都没有,也可以有一堆) */ Collections.addAll(list, 1,2,3,4,5,56,6,4,3,5,2); System.out.println(list); //不是只有TreeSet才可以进行排序操作 //List这边的集合以可以 //Collections.sort(list);//升序 //System.out.println(list); //重载方法 // Collections.sort(list, new Comparator<Integer>() { // // @Override // public int compare(Integer o1, Integer o2) { // // return o2-o1; // } // });//降序 //降序快捷方式,这个方式不适合自定义类的排序 Collections.sort(list, Collections.reverseOrder()); System.out.println(list); //static void shuffle(List<?> list) // 使用默认随机源对指定列表进行置换 //3个51 - 17 1次一个呢只能有1张 Collections.shuffle(list); System.out.println(list); //集合中查找某个元素--> 二分查找 /* * 第一个参数是要查找的集合 * 第二个参数要查找的值 *找到就是下标 找不到-1的值 *一定要先排序 */ Collections.binarySearch(list, 1); // static void reverse(List<?> list) // 反转指定列表中元素的顺序。 //若需要线程安全时,只需要通过Collections中的方法即可 完成转换 /* * static <T> Collection<T> synchronizedCollection(Collection<T> c) 返回指定 collection 支持的同步(线程安全的)collection。 static <T> List<T> synchronizedList(List<T> list) 返回指定列表支持的同步(线程安全的)列表。 static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) 返回由指定映射支持的同步(线程安全的)映射。 static <T> Set<T> synchronizedSet(Set<T> s) 返回指定 set 支持的同步(线程安全的)set。 * * */ List<Integer> safeList = Collections.synchronizedList(list); System.out.println("当前线程安全的集合是:"+(safeList instanceof Vector)); } }