泛型
泛型的概述
广泛的类型,在定义一个类的时候,类型有些方法参数,返回值类型不确定时,就使用一个符号,表示这些尚未确定的类型,就称为泛型。
泛型的好处
提高了数据的安全性,将运行时的问题提前暴露在了编译阶段。
避免了强转的麻烦
泛型
泛型类 泛型方法 泛型接口
说明
类名后面跟着的泛型类型,是泛型的声明,一旦泛型声明出来,就相当于成为了已知类型,这个类型在整个类中都可以使用。
泛型的声明,只需要一个合法的标识符,常用字母:T E K V Q ..
泛型确定的时机:将来在使用这个类创建对象的时候。
泛型类实例一(自定义队列):
import java.util.LinkedList;
//自定义队列(使用泛型,自定异常)
public class Test01Generic {
public static void main(String[] args) throws Exception {
MyQueue<Integer> mq = new MyQueue<Integer>();
mq.in(12);
mq.in(44);
mq.in(9);
mq.out();
mq.out();
mq.out();
mq.out();
}
}
class MyQueue<E>{
private LinkedList<E> linkedList = new LinkedList<E>();
//必须声明一个链表对象,不然不知道指向哪里,因为要在堆内存中开辟空间存储元素
public void in(E e) {
linkedList.addLast(e);
}
public E out() throws Exception {
if(isEmpty()) {
throw new MyQueueEmptyException("队列空了哦亲");
}
return linkedList.removeFirst();
}
public boolean isEmpty() {
return linkedList.isEmpty();
}
}
class MyQueueEmptyException extends Exception{
public MyQueueEmptyException(String str) {
super(str);
}
}
泛型类实例二:
需求:模拟ArrayList定义MyArrayList 完成add、get和size方法
感觉这个题挺牛逼的,就是吧,一直在用ArrayList,感觉用着很棒,然后突然发现其实内部实现很简单,自己也可以写那种感觉
/*需求:模拟ArrayList定义MyArrayList 完成add、get和size方法*/
public class Test02Generic {
public static void main(String[] args) {
MyArrayList<Integer> list = new MyArrayList<Integer>();
list.add(123);
list.add(456);
list.add(789);
System.out.println(list.get(0));
System.out.println(list.size());
list.get(5);
}
}
class MyArrayList<E>{
//定义一个对象数组,存储元素
private Object[] objs;
private int size;//定义一个变量,存储数组的大小
public MyArrayList() {
//定义构造方法
//定义,在新建一个MyArrayList类时,默认开辟一个size为10的空间
objs = new Object[10];
}
public void add(E e) {
if(size >= objs.length) {
Object[] oldObjs = objs;
objs = new Object[objs.length+10];
}
objs[size] = e;
size++;
}
public int size() {
return size;
}
public E get(int index) {
if(index>=size) {
throw new RuntimeException("没那么多元素");
}
return (E)objs[index];
}
}
泛型通配符
使用泛型的时候,没有使用具体的泛型声明T,而是使用了和声明过的某个泛型T有关的一类类型,就称为泛型的通配符。三种形式:
第一种形式,使用?来表示可以是任意类型,
例如:Collection<E>接口中的removeAll(Collection<?> c),表示可以接收任意泛型类型的集合,作为该方法的实际参数。参数集合的泛型,可以是与E没有任何关系
第二种形式,使用? extends E来表示必须是某个泛型类型或是该泛型类型的子类,例如:Collection<E>接口中的addAll(Collection<? extends E> c),表示可以接收泛型类型是调用者泛型类型(这里为E)或者其子类的集合,作为该方法的实际参数。参数的泛型和调用者的泛型,必须有关(相同或者是子父类)。确定了泛型的上边界。
第三种形式,使用? super E来表示必须是某个泛型类型或者是该泛型类型的父类,例如:TreeSet<E>集合中,存储的都是E类型的元素,构造方法TreeSet(Comparator<? super E> com),表示可以接收泛型类型是集合元素类型(这里为E)或者是元素类型的父类的比较器,作为构造方法的参数。参数的泛型和集合的泛型必须有关(相同或者是子父类)。确定了泛型的下边界。
注: ? extends E: 接收E类型或者E的子类型。
? super E: 接收E类型或者E的父类型。
泛型方法
1、在方法上声明的泛型,可以在整个方法中,当做已知类型来使用。
2、如果【非静态】方法上没有任何泛型的声明,那么可以使用类中定义的泛型
3、如果【静态】方法上没有任何的泛型声明,那么就不能使用泛型,连类中定义的泛型,也不能使用,因为类中的泛型需要在创建对象的时候才能确定。所以【静态】方法想使用泛型,就必须在自己的方法上单独声明。
public class Test03Generic {
public static void main(String[] args) {
String x = new Test03Generic().getData("wkaaka");
System.out.println(x);
//输出结果:wkaaka
}
private <T> T getData(T data) {
return data;
}
}
泛型接口
在接口声明上,定义好的泛型,可以在整个接口中当做已知类型来使用。
2、泛型接口被其他类实现的时候,有两种实现方式:
(1)声明的类不再是一个泛型类,而是一个确定了泛型的类,格式如下: class 实现类类名 implements 接口名<具体类型> {
所有的泛型类型都已经被确定
}
(2)声明的类还是一个泛型类,泛型的类型和接口的泛型一致,格式如下:
class 实现类类名<泛型标识符1> implements 接口名<泛型标识符1> {
所有的方法还都是可以使用泛型标识符的方法
}
public class Test04 {
public static void main(String[] args) {
MyInter<String> my = new MyInter<String>();
my.print("泛型");
MyInter2 my2 = new MyInter2();
my.print("只能传字符串");
}
}
interface Inter<T>{
void print(T t);
}
//实现不知为何类型时,可以这样定义
class MyInter<T> implements Inter<T>{
public void print(T t) {
System.out.println("myprint:" + t);
}
}
//使用接口时明确具体类型
class MyInter2 implements Inter<String>{
public void print(String t) {
System.out.println("myprint:" + t);
}
}
Collections工具类
Collections常用功能
List排序:sort方法
Collection元素搜索:binarySearch方法
改变Collection中的元素:addAll方法
|
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Test01Collections {
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
list.add(11);list.add(15);
list.add(16);list.add(19);
list.add(222);list.add(335);
System.out.println(Collections.binarySearch(list, 12));//针对list集合的方法
//结果-2
//前提:list集合中的元素必须是升序的
//其次,搜索关键字的索引,如果它包含在列表中; 否则, (-(insertion point) - 1)
//Collections.fill(list,10);//针对list集合的方法
//将list集合中所有元素都填充为10
System.out.println(Collections.frequency(list, 11));//针对所有单列集合的方法
//运行结果:1
Collections.replaceAll(list, 10, 8);//针对list集合,返回boolean值
//没有10,返回false
//System.out.println(Collections.replaceAll(list, 10, 8));
System.out.println(Collections.max(list));//针对所有单列集合的方法
//运行结果:335
System.out.println(Collections.min(list));//针对所有单列集合的方法
//运行结果:11
Collections.reverse(list);//针对list集合的方法
System.out.println(list);
//Collections.shuffle(list);//针对list集合的方法
//System.out.println(list);
Collections.swap(list, 0, 1);
System.out.println(list);
}
}
Map
Map:双列集合的顶层接口。
Map:单词含义,地图。地图上的每个点,都表示了生活中的一个具体位置。地图的点和生活中的位置,有一个一一对应的关系,这种关系是通过穷举的方式来描述的。
Map是一个以键(key)值(value)对形式存储数据的容器。
Map的特点:
Map 是一个接口,但不是Collection的子接口。
Map以键值对的形式存储数据,每个键(key)对应一个值(value)。
Map 中的键不能重复(唯一)。
Map 中的值可以重复。
主要实现类 HashMap Hashtable TreeMap ...
Map结构图:
Map中常用方法:
put(K key, V value) 向Map中添加数据
putAll(Map<? extends K,? extends V> m) 向Map中添加另一个Map集合。
isEmpty() Map中是否包含数据
size() Map中包含键值对的个数
get(key) 根据key获取value
clear() 清空Map
containsKey(key) 判断Map中是否包含key
containsValue(value) 判断Map中是否包含value
keySet() 返回Map中所有key 组成的Set集合
values() 返回Map中所有value组成的集合 (返回值Collection类型)
entrySet() 返回此映射中包含的映射关系的 Set 视图
HashMap
HashMap是Map接口的最常用实现类,底层是哈希表形式存储的,非线程安全的,允许有null键值对。
Map遍历
方式一:使用keySet()方法
- 获取Map集合中的所有键,放到一个Set集合中,遍历该Set集合,获取到每一个键,根据键再来获取对应的值。【根据键获取值】
- 获取Map集合中的所有键
Set<K> keySet()
方式二:使用entrySet()方法
获取Map集合中的所有键值对对象(Entry),到Set集合中,遍历Set集合,拿到的是每个键值对对象(Entry),从这个对象中分别获取键和值。【根据键值对对象获取键和值】
根据Map集合获取所有的键值对对象,到一个Set集合中
Set<Map.Entry<K, V>> entrySet()
Entry是Map接口中的内部接口,访问的方式:Map.Entry
Entry的常用方法:
getKey()获取当前键值对对象的键
getValue()获取当前键值对对象的值
案例1:编写一个方法,完成英译汉翻译功能。
dog--->狗
cat--->猫
pig--->猪
monkey--->猴
cow---> 牛
import java.util.HashMap;
import java.util.Map;
/*编写一个方法,完成英译汉翻译功能。
dog--->狗
cat--->猫
pig--->猪
monkey--->猴
cow---> 牛*/
public class Test01Map {
//分析:英译汉即输入英文,翻译出中文,可以使用Map集合存储数据,相当于一个字典,输入键,得到值
public static void main(String[] args) {
String tran = translate("do");
System.out.println(tran);
}
private static String translate(String string) {
Map<String,String> map = new HashMap<String,String>();
map.put("dog", "狗");
map.put("cat", "猫");
map.put("pig", "猪");
map.put("monkey", "猴");
map.put("cow", "牛");
String str = map.get(string);
if(str==null) {
return "词典里面暂时没有收录这个单词哦!";
}
return map.get(string);
}
}
案例2:键盘录入一个字符串,统计每个字符出现的次数
例如,录入aaaabbccddd!@#@#$@#$%cc66ff
打印出来:a有4个,b有2个,c有4个,d有3个,!有1个,@有3个,$有2个,%有1个,6有2个,f有2个
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Scanner;
/*案例2:键盘录入一个字符串,统计每个字符出现的次数
例如,录入aaaabbccddd!@#@#$@#$%cc66ff
打印出来:a有4个,b有2个,c有4个,d有3个,!有1个,@有3个,$有2个,%有1个,6有2个,f有2个*/
public class Test02Map {
/*分析:要存储的数据,有字符和字符个数,优先考虑双列集合Map*/
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个字符串:");
String str = sc.nextLine();
char[] chs = str.toCharArray();
Map<Character,Integer> map = new HashMap<Character,Integer>();
for(int n = 0; n < chs.length;n++) {
char c = chs[n];
map.put(c,map.containsKey(c)?map.get(c)+1:1);
}
StringBuilder sb = new StringBuilder();
for(Entry<Character, Integer> entry : map.entrySet()) {
sb.append(entry.getKey()+"有" +entry.getValue()+"个,");
}
System.out.println(sb.substring(0,sb.length()-1));//返回值为字符串
}
}
Hashtable
Hashtable是一个比较古老的类(从JDK1.2开始),特点是线程安全的,不允许有null键值对。用法和HashMap相同,基本被HashMap替代。
面试常见问题HashMap和Hashtable的区别?
HashMap: 线程不安全,允许null键值对
Hashtable: 线程安全,不允许null键值对
TreeMap
特点:会根据key值进行升序排列。
/*
* 底层是由红黑数(自平衡的二叉树)来实现的 会根据key升序排序
* key需要有排序的能力 或传入比较器
* */
TreeMap构造方法:
TreeMap() 使用其键的自然排序构造一个新的空树状图。 |
TreeMap(Comparator<? super K> comparator) 构造一个新的,空的树图,按照给定的比较器排序。 |
TreeMap(Map<? extends K,? extends V> m) 构造一个新的树状图,其中包含与给定地图相同的映射,根据其键的 自然顺序进行排序 。 |
TreeMap(SortedMap<K,? extends V> m) 构造一个包含相同映射并使用与指定排序映射相同顺序的新树映射。 |
LinkedHashMap
是HashMap的一个子类。和HashMap的不同之处在于,具有可预知的迭代顺序,存储键值对的顺序和遍历集合时取出键值对的顺序一致。
斗地主练习
斗地主的制牌、洗牌和发牌
思路
1、制牌:1~K一共13个数字,四个花色,4 * 13张牌,小王、大王
2、洗牌:shuffle将集合中的字符串随机置换
3、发牌:将一个集合中的元素,分发到3个集合中
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/*斗地主的制牌、洗牌和发牌
思路
1、制牌:1~K一共13个数字,四个花色,4 * 13张牌,小王、大王
2、洗牌:shuffle将集合中的字符串随机置换
3、发牌:将一个集合中的元素,分发到3个集合中*/
public class Test03 {
public static void main(String[] args) {
//第一种写法
Map<Integer,String> pokerMap = new HashMap<Integer,String>();
String[] colors = {"♥","♠","♣","♦"};
String[] nums = {"2","A","K","Q","J","10","9","8","7","6","5","4","3"};
int id = 0;//牌的编号
pokerMap.put(id++, "大王");//编号从0到53,编号最好从0开始,因为数组是从0开始的
/*count与count++的关系区别一下.count+1,结果为count+1;而count++虽然值也为count+1
但在运算的时候,先使用count的值,再count+1,注意区分*/
pokerMap.put(id++, "小王");
for(String color : colors) {
for(String num : nums) {
pokerMap.put(id++, color + num);
}
}
Set<Integer> set = pokerMap.keySet();
List<Integer> listId = new ArrayList<Integer>();
listId.addAll(set);
Collections.shuffle(listId);//Collections的这个方法只有List集合能用
List<Integer> player1Id = new ArrayList<Integer>();
List<Integer> player2Id = new ArrayList<Integer>();
List<Integer> player3Id = new ArrayList<Integer>();
List<Integer> dipaiId = new ArrayList<Integer>();
for(int n = 0; n < listId.size();n++) {
if(n >= 51) {
dipaiId.add(listId.get(n));
}
if(n % 3 == 0) {
player1Id.add(listId.get(n));
}
else if(n % 3 == 1) {
player2Id.add(listId.get(n));
}
else if(n % 3 == 2) {
player3Id.add(listId.get(n));
}
}
List<String> player1 = new ArrayList<String>();
List<String> player2 = new ArrayList<String>();
List<String> player3 = new ArrayList<String>();
List<String> dipai = new ArrayList<String>();
for(Integer n : dipaiId) {
dipai.add(pokerMap.get(n));
}
for(Integer n : player1Id) {
player1.add(pokerMap.get(n));
}
for(Integer n : player2Id) {
player2.add(pokerMap.get(n));
}
for(Integer n : player3Id) {
player3.add(pokerMap.get(n));
}
System.out.println(player1);
System.out.println(player2);
System.out.println(player3);
System.out.println(dipai);
//第二种写法:感觉这种不太好,不过更简单
/*List<String> poker = new ArrayList<String>();
String[] colors = {"♥","♠","♣","♦"};
String[] nums = {"2","A","K","Q","J","10","9","8","7","6","5","4","3"};
for(String color : colors) {
for(String num : nums) {
poker.add(color + num);
}
}
Collections.shuffle(poker);
List<String> player1 = new ArrayList<String>();
List<String> player2 = new ArrayList<String>();
List<String> player3 = new ArrayList<String>();
List<String> dipai= new ArrayList<String>();
for(int i = 1;i <= 3;i++) {
dipai.add(poker.remove(0));
}
while(true) {
if(!poker.isEmpty()) {
player1.add(poker.remove(0));
}
if(!poker.isEmpty()) {
player2.add(poker.remove(0));
}
if(!poker.isEmpty()) {
player3.add(poker.remove(0));
}else {
break;
}
}
System.out.println(player1);
System.out.println(player2);
System.out.println(player3);
System.out.println(dipai);*/
}
}