java学习笔记Day6:集合泛型枚举

1.集合泛型枚举

1.1为什么使用集合框架

场景1:存储一个班学员信息,假定一个班容纳20名学员

解决办法:一维数组

场景2:如何存储每天的新闻信息?每天的新闻总数不确定,太少浪费空间,太多空间不足

解决办法:如果并不知道程序运行时会需要多少对象,或者需要更复杂方式存储对象——可以使用Java集合框架

1.2Java集合框架包含的内容

在这里插入图片描述

1.2.1集合框架

Java集合框架提供了一套性能优良、使用方便的接口和类,它们位于java.util包中

1.2.2Collection

Collection接口存储一组不唯一,无序的对象

1.2.3List 有序不唯一

定义:List 接口存储一组不唯一,有序(插入顺序)的对象

1.2.4Set 无序,唯一

定义:Set 接口存储一组唯一,无序的对象,唯一:是指equals相同

使用:遍历set

  1. 获取迭代器

获取Iterator :Collection 接口的iterate()方法
Iterator的方法
boolean hasNext(): 判断是否存在另一个可访问的元素
Object next(): 返回要访问的下一个元素

  1. 增强for循环遍历
1.2.5Map:键值对

定义:Map接口存储一组键值对象,提供key到value的映射

Key:具有唯一性,且无序

  1. 如果再次放入某个key相同的值,那么value覆盖原来的值

  2. key可以是null:但是null值也是一个唯一存在的key

Value:可以重复,可以是任意值

获取所有key和所有map

遍历Map:

方法1:通过迭代器Iterator实现遍历
方法2:增强型for循环

常用方法:

方法名说明
Object put(Object key, Object val)以键-值对的方式进行存储
Object get(Object key)根据键返回相关联的值,如果不存在指定的键,返回null
Object remove(Object key)删除由指定的键映射的键-值对
int size()返回元素个数
Set keySet()返回键的集合
Collection values()返回值的集合
boolean containsKey(Object key)如果存在由指定的键映射的键-值对,返回true
1.2.6 List的实现类
1.2.6.1ArrayList

定义:ArrayList实现了长度可变的数组,在内存中分配连续的空间。遍历元素和随机访问元素的效率比较高

常用方法:

方法名说明
boolean add(Object o)在列表的末尾顺序添加元素,起始索引位置从0开始
void add(int index,Object o)在指定的索引位置添加元素。索引位置必须介于0和列表中元素个数之间
int size()返回列表中的元素个数
Object get(int index)返回指定索引位置处的元素。取出的元素是Object类型,使用前需要进行强制类型转换
boolean contains(Object o)判断列表中是否存在指定元素
boolean remove(Object o)从列表中删除元素
Object remove(int index)从列表中删除指定位置元素,起始索引位置从0开始

代码演练:

public static void main(String[] args) {
    List l = new ArrayList();
//		l.add("a");
    l.add("b");
    l.add("c");
    List l2 = new ArrayList();
    l2.add("a");
    l2.add("a");
//1.判断是否存在某个元素:返回true的前提是equals判单相等
System.out.println(l.contains("a"));
System.out.println(l.containsAll(l2));//只要元素包含
//2.添加
l.addAll(l2);
l.addAll(l2);
l.addAll(l2);
System.out.println(Arrays.toString(l.toArray()));
//3.获取元素的游标
//or -1 if there is no such index.
//从左到右第一次遇到的那个
System.out.println(l.indexOf("a"));
//从右到左第一次遇到的那个
System.out.println(l.lastIndexOf("c"));
//截取:从fromIndex开始,到toIndex游标之前一位
List l3 = l.subList(0, 2);
System.out.println(Arrays.toString(l3.toArray()));
}
1.2.6.2LinkedList

定义:LinkedList采用双向链表存储方式。插入、删除元素时效率比较高

常用方法:

方法名说明
void addFirst(Object o)在列表的首部添加元素
void addLast(Object o)在列表的末尾添加元素
Object getFirst()返回列表中的第一个元素
Object getLast()返回列表中的最后一个元素
Object removeFirst()删除并返回列表中的第一个元素
Object removeLast()删除并返回列表中的最后一个元素

使用:因为该实现类,实现了很多List接口中没有的方法,所以通常使用该类本身来声明

代码演练:

public static void main(String[] args) {
	LinkedList list = new LinkedList();
	list.addFirst("");
	list.addLast("");
	list.getFirst();
	list.getLast();
	list.removeFirst();
	list.removeLast();
}
1.2.6.3 LinkedList VS ArrayList

ArrayList:不适合频繁增减元素, 擅长随机访问

LinkedList:适合于增减元素操作,不擅长随机访问

1.2.7Set接口的实现类
1.2.7.1HashSet
Set set=new HashSet();
String s1=new String("java");
String s2=s1;
String s3=new String("JAVA");
set.add(s1);
set.add(s2);
set.add(s3);
System.out.println(set.size());

HashSet使用的是HashMap的Key

代码演练:

public static void main(String[] args) {
	//无序且唯一
	//基本数据类型/引用数据类型
	Set s = new HashSet();
	s.add("a");
	s.add("a");
	s.add("a");
	s.add("b");
	s.add("c");
	s.add("d");
	
	//1.获取迭代器
	Iterator is = s.iterator();
	while(is.hasNext())//判断是否有下一个:如果有则返回true,没有则返回false
	{
		Object obj = is.next();
		System.out.println(obj);
	}
	//2.增强for循环来遍历
	for(Object obj : s){
		System.out.println(obj);
	}
}
1.2.8 Map的实现类
1.2.8.1HashMap
1.2.8.1.1代码演练
public static void main(String[] args) {
		Map m = new HashMap();
		//1.添加元素
		m.put("k1", "v1");
		m.put("k1", "value1");
		System.out.println(m.get("k1"));
		m.put(null, null);
		m.put(null, "nullStr");
		System.out.println(m.get(null));
		//2.获取所有key
		Set keys = m.keySet();
		System.out.println(Arrays.toString(keys.toArray()));
		//3.获取所有value
		Collection cons = m.values();
		System.out.println(Arrays.toString(cons.toArray()));
	}
1.2.9Hash,set和map(理解)

① hashMap

  1. Entry:是一个单向链表

  2. TreeEntry:包含了hash的双向链

  3. 红黑树

② Hash:

  1. hash可以重写

  2. native默认情况下,hash是由本地调用提供的,比如调用本地C。

  3. 集合中的hashcode是被****重写过****的

public class Test5 {
	public static void main(String[] args) {
		System.out.println("a".hashCode());
		System.out.println(Objects.hash(1));//基本数据类型的hash
		System.out.println(Objects.hash(1));//基本数据类型的hash
		System.out.println(Objects.hash(1));//基本数据类型的hash
		//相同对象的hash,必然一样
		A a1 = new A();
		A a2 = a1;
		System.out.println(a1.hashCode());
		System.out.println(a2.hashCode());
		//不同对象的hash,不一样
		System.out.println(Objects.hash(new A()));
		System.out.println(Objects.hash(new A()));
		//set的元素一致,hash就一致
		Set s1 = new HashSet();
		s1.add("a");
		s1.add("b");
		s1.add("c");
		Set s2 = new HashSet();
		s2.add("a");
		s2.add("b");
		s2.add("c");
		System.out.println(s1.hashCode());
		System.out.println(s2.hashCode());
		//Map的key 和 value相同,那么他们hash也一样
		Map m1 = new HashMap();
		m1.put("k1", "value1");
		m1.put("k2", "value2");
		m1.put("k3", "value3");
		Map m2 = new HashMap();
		m2.put("k1", "value1");
		m2.put("k2", "value2");
		m2.put("k3", "value3");
		System.out.println(m1.hashCode());
		System.out.println(m2.hashCode());
		//LinkedList
		LinkedList l1 = new LinkedList();
		l1.add("a");
		l1.add("b");
		l1.add("c");
		LinkedList l2 = new LinkedList();
		l2.add("a");
		l2.add("b");
		l2.add("c");
		System.out.println(l1.hashCode());
		System.out.println(l2.hashCode());
		//ArrayList
		List la1 = new ArrayList();
		la1.add("a");
		la1.add("b");
		la1.add("c");
		List la2 = new ArrayList();
		la2.add("a");
		la2.add("b");
		la2.add("c");
		System.out.println(la1.hashCode());
		System.out.println(la2.hashCode());
	}

}
class A{}
1.2.10泛型
1.2.10.1应用场景

如何解决以下强制类型转换时容易出现的异常问题

List的get(int index)方法获取元素
Map的get(Object key)方法获取元素
Iterator的next()方法获取元素

通过泛型
JDK5.0使用泛型改写了集合框架中的所有接口和类

目的:统一集合当中的元素

1.2.10.2定义

将对象的类型作为参数,指定到其他类或者方法上,从而保证类型转换的安全性和稳定性
本质是参数化类型

1.2.10.3泛型集合

泛型集合可以约束集合内的元素类型
典型泛型集合ArrayList、HashMap<K,V>
、<K,V>表示该泛型集合中的元素类型
泛型集合中的数据不再转换为Object

1.2.10.4泛型类

泛型类用于类的定义中,被称为泛型类。通过泛型可以完成对一组类的操作对外开放相同的接口。最典型的就是各种容器类,如:List、Set、Map

(1)泛型标识:标识可以随便写,通常是一个大写字母,需要具备一定的可读性

(2)比如:T(type),I(item),K(key),V(value)

(3)如果在泛型类当中没有指定泛型,那么该类型,默认为Object

(4)Public void test(T t){}:这种不是泛型方法!!!!!!

(5)通常来说,我们不会对已泛型方式传入的数据做什么操作,因为使用泛型的前提是,我不知道用户会提供什么类型给我,所以,不会去操作他

(6) :泛型形参

(7) :泛型实参

1.2.10.5泛型接口

泛型接口与泛型类的定义及使用基本相同,泛型接口常被用在各种类的生产器中

(1) 泛型接口是定义在接口上

(2) 泛型类和泛型接口的泛型,在实现或者继承的情况下,重写或者实现的方法,泛型的参数和返回值需要保持一致

(3) 泛型接口的泛型实参必须指定,而实现类,可以不必指定,如果不指定,则沿用其接口的泛型类型

//定义一个泛型接口
public interface Generator<T> {
    //接口方法
	public T next();
}
public interface List <E>{
	public void add(E e);
	public void set(int i,E e);
	public E get(int i);
	public E remove(int i);
	public int size();
	public boolean isEmpty();
	public E[] toArray();
}
1.2.10.6泛型通配符

我们都知道Integer是Number的子类,那么阅读以下代码,思考会出现什么结果

public static void main(String[] args) {
	test(new ArrayList<Number>());
}
static void test (List<?> is){}

(1) ?:代表任意泛型

(2) ?:是泛型实参,不是泛型形参,可以看成是所有类型的父类型

(3) 可以指定边界,规定某一批类型可以作为泛型,既可以规定泛型形参,也可以规定泛型实参

1.2.10.7泛型边界

是在调用方法的时候指明泛型的具体类型

? super B ?用于指定类型实参的边界

T extends Object T用于指定类型形参的边界

public class Test1{
	public static void main(String[] args) {		
	Demo1<String> d1 = new Demo1<>();
	d1.put("你好,范特西");
	System.out.println(d1.get());
	}
}
//这是一个泛型类
class Demo1<T extends Object>{
	//这个不是泛型方法!!!!!!!!
	private Object obj;
	public void put(T t){
		obj = t;
	}
	public T get(){
		return (T) obj;
	}
}
public class Test2{
	public static void main(String[] args) {
		Demo1<A> d1 = new Demo1<A>();
		Demo2 d2 = new Demo2();
		d2.test(d1);
	}
}

//这是一个泛型类
class Demo2{
	public void test(Demo1<? super B> demo1){	
	}
}

class A{}
class B extends A{}
class C extends A{}
1.2.10.8泛型方法

是在调用方法的时候指明泛型的具体类型

public class Test3{
	public static void main(String[] args) {
		DemoUtil util = new DemoUtil();
	}
}

class DemoUtil<T , E>{
	//这是一个泛型方法
	public <K , V , I> void  test(K k , V v , I i , T t ,E e){
	}
}

(1) 如果一个类当中,不必多个方法公用同一个类型,某个方法的泛型是独立在当前方法当中的,这时,建议使用泛型方法,不影响到其他方法的类型

(2) 在方法的修饰符和返回值之间,申明泛型,该泛型,只作用于当前方法

1.2.10.4使用

① 对于基本数据类型:泛型只能使用包装类来定义基本数据类型

1.2.10.5代码演练
public static void main(String[] args) {		
		List<String> l = new ArrayList<String>();
		l.add("ybb1");
		l.add("ybb2");
		l.add("ybb3");
		l.add("ybb4");		
		for(String str : l){
			System.out.println(str);
		}		
		Set<Double> s = new HashSet<Double>();		
		Map<String,Object> m = new HashMap<String,Object>();		
	}
public class ArrayList<E> implements List<E>{
	//希望通过该实现类,来完成一个可变长度的数组
	private Object[] objs = new Object[0];//这是一个长度初始为0的object数组
	private int length = 0;//是当前list的长度
@Override
public void add(E e) {
	// 通过复制数组,来完成对数组的扩容
	objs = Arrays.copyOf(objs, ++length);
	objs[length - 1] = e;
}

@SuppressWarnings("unchecked")
@Override
public E get(int i) {//取游标位置的值
	//1.先判断取值范围是否合理
	if(i < 0 || i > (length - 1)){
		throw new RuntimeException("游标越界异常:"+i);
	}
	return (E) this.objs[i];
}

@Override
public E remove(int i) {
	//1.先判断取值范围是否合理
	if(i < 0 || i > (length - 1)){
		throw new RuntimeException("游标越界异常:"+i);
	}
	Object removeObj = this.objs[i];//要删除的旧值
	for(int j = i + 1 ; j < length ; j ++){
		this.objs[j-1] = this.objs[j];
	}
	objs = Arrays.copyOf(objs, --length);
	return (E) removeObj;
}

@Override
public int size() {
	return this.length;
}

@Override
public boolean isEmpty() {
	return this.length == 0;
}

@Override
public E[] toArray() {//将list变为数组
	// 复制一个数组,不能直接给原数组,直接给原数组那么就意味者,获取到原的地方,可以任意修改集合当中的数据,造成数据隐患
	return (E[]) Arrays.copyOf(objs, length);
}

@Override
public void set(int i, E e) {
	//1.先判断取值范围是否合理
	if(i < 0 || i > (length - 1)){
		throw new RuntimeException("游标越界异常:"+i);
	}
	this.objs[i] = e;
}
}
1.2.11枚举
1.2.11.1应用背景

问题:

如果我需要定义一个类型,例如:性别
int sex = 0;//0:女/1:男
以上定义方式有什么问题?
如何解决

1.2.11.2定义

枚举是一种特殊的数据类型,之所以特殊是因为它既是一种类(class)类型却又比类型多了些特殊的约束,但是这些约束的存在也造就了枚举类型的简洁,安全性以及便捷性。

(1) 一些散列值:如果存在数据的安全风险,那么在使用的过程中,该数据可以定义为枚举来避免风险数据对安全性的影响

(2)构造是针对所有的枚举的元素,每一个枚举的元素都相当于一个类。

(3)构造必须私有,调用构造的方法是通过调用类型

(4) 枚举中的类型是单例,有几个类型创建几个单例(问题之一)

(5) 枚举可以重写方法

(6) 枚举可以实现接口

(7) 有可能会大量产生单例,但是这个问题并不是枚举最致命的问题

(8) 在网络当中,WEB开发,枚举和很多主流的数据结构不兼容

1.2.11.3创建枚举

创建一个枚举类型,需要使用关键字enum

public enum TESTENUM {

}
1.2.11.4定义一个枚举,并使用其完成赋值
public enum TESTENUM {
	F1,
	F2,
	F3
}
TESTENUM te = TESTENUM.F1
1.2.11.5在switch中使用枚举
TESTENUM te = TESTENUM.F1
switch(te){
    case F1:
        break;
    case F2:
        break;
}
1.2.11.6在类当中使用枚举作为属性
private  TESTENUM te;
public TESTENUM getTe(){
    return te;
}
public void setTe(TESTENUM te){
    this.te = te;
}
1.2.11.7枚举实现接口
public enum MyDay implements DayInterface
1.2.11.8接口组织枚举
public interface A {
	enum E1 implements A{
		T1,T2,T3
	}
	enum E2 implements A{
		I1,I2,I3
	}
}
public static void main(String[] args) {
    A.E1 e1 = A.E1.T1;
    A.E1 e2 = A.E1.T2;
}
1.2.11.9枚举优缺点

优点:
可以有效的管理离散值
标识符的缺点:
代码可读性差,易用性差。
类型不安全。
耦合性高,扩展性差。

缺点:
通用性差,在web工程中和其他一些不支持枚举的第三方交互时,用枚举不如用xml或properties或JSON
需要实例化,会大量消耗资源,得不偿失

注意:相较于枚举的优点,其缺点更加明显,所以不要盲目跟风,理性看待新技术,旧的未必不好,新的未必有用,一切从实践出发!

1.2.11.7综合代码演练
public interface DefInterface {
	public void test();
}
public class Person {
	private int sex;
	private Sexs sexs;
	public int getSex() {
		return sex;
	}
	public void setSex(int sex) {
		this.sex = sex;
	}
	public Sexs getSexs() {
		return sexs;
	}
	public void setSexs(Sexs sexs) {
		this.sexs = sexs;
	}
}
public interface Sex {
	public int MAN = 1;
	public int WOMAN = 0;
}
public enum Sexs implements DefInterface{
	MAN(1){
		public String toString(){
			return "sex:男";
		}
		//在枚举中的方法重写        
		@Override
		public void test() {
			// TODO Auto-generated method stub
		}
	},WOMAN(0){
		public String toString(){
			return "sex:女";
		}
		@Override
		public void test() {
			// TODO Auto-generated method stub
		}
	},MID(2){
		public String toString(){
			return "sex:脑补";
		}
		@Override
		public void test() {
			// TODO Auto-generated method stub
		}
	};
	private int sex;
	private Sexs(int sex){
		this.sex = sex;
	}
    //在枚举中创建方法(和构造方法)
	private Sexs(){
		System.out.println("构造被调用了");
	}
	public int getSexValue(){
		return this.sex;
	}
	public String toString(){
		return "sex:"+this.sex;
	}
}
public static void main(String[] args) {
		// TODO Auto-generated method stub
//		Person p = new Person();
//		p.setSex(Sex.MAN);
//		p.setSex(2);//这是潜在的风险
//		
//		p.setSexs(Sexs.MAN);
		
//		Sexs s = Sexs.MAN;
//		switch(s){
//			case MAN:
//				break;
//			case WOMAN:
//				break;
//		}
		Sexs s1 = Sexs.MID;//第一次是调用了2次
		System.out.println(s1.getSexValue());
		System.out.println(s1);
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值