Java基础--集合

目录

 

常用集合的分类

一 Collection

1 Collection集合的方法:

2 集合特点

3 集合的功能

4 集合的遍历

1 iterator 迭代器

2 并发修改异常

3 练习

二 泛型

1 泛型的引入

2 泛型类

3泛型接口

4 泛型方法

5 泛型限定符

6 泛型嵌套

7 泛型案例

三 list接口

1 Collection将集合划分为两大类:

2 List接口的遍历

1 List接口的遍历方式

2 List接口去除重复元素

3 并发修改异常的处理:

3 ArrayList

1 ArrayList

2 Vector

3 Stack

四 Queue

1 Queue

2双端列操作(Deque)

3 ArrayDeque

4 LinkedList

5 Set接口

1 Set接口的特点:

2 HashSet

3 TreeSet

六 map

1 map 双列集合

1 Map的特点:

2 map方法

3 map 的遍历方式

2 HashMap

3 TreeMap

4 Hashtable

5 LinkedHashMap

6 WeakHashMap

七 Enum(枚举)

1 Enum 的概念

2 Enum的特点

 3 Enum常用方法

4 Enum 练习


常用集合的分类

Collection 接口的接口 对象的集合(单列集合)  
├——-List 接口:元素按进入先后有序保存,可重复  
│—————-├ LinkedList 接口实现类, 链表, 插入删除, 没有同步, 线程不安全  
│—————-├ ArrayList 接口实现类, 数组, 随机访问, 没有同步, 线程不安全  
│—————-└ Vector 接口实现类 数组, 同步, 线程安全  
│ ———————-└ Stack 是Vector类的实现类 
└——-Set 接口: 仅接收一次,不可重复,并做内部排序 
├—————-└HashSet 使用hash表(数组)存储元素 
│————————└ LinkedHashSet  链表维护元素的插入次序 
└—————-TreeSet 底层实现为二叉树,元素排好序

Map 接口 键值对的集合 (双列集合) 
├———Hashtable 接口实现类, 同步, 线程安全  
├———HashMap 接口实现类 ,没有同步, 线程不安全- 
│—————–├ LinkedHashMap 双向链表和哈希表实现 
│—————–└ WeakHashMap 
├ ——–TreeMap  红黑树对所有的key进行排序 
└———IdentifyHashMap

一 Collection

1 Collection集合的方法:

2 集合特点

1.集合能够对数据进行增加删除修改查询的操作
2.集合能够存储引用类型,如果是基本类型可以是包装类类型
3.集合的长度是可变的
--------------------数据结构------------------------
4.部分集合是有序的,部分集合是无序的 (这里的有序指的是存储有序,并不是排序)
5.部分集合是唯一的,部分集合是可重复 (11, 22 ,33, 33, 22)
6.部分集合是可排序的,部分集合是不可排序的    33 44 55 11 -> 11 33 44 55
7.部分集合是线程安全的,部分集合是线程不安全  (synchronized)
集合应该设计成一个类,还是一个框架?
集合框架应该设计成一个接口,不同的集合的实现不一样,那么效率不一样,
特点不一样,我们可以自由选取
数据结构: 数据的存储方式。
常见的和集合相关的数据结构: 数组,栈,队列,链表,哈希表,二叉树
存储方式不一样决定了该集合的性能效率不一样

3 集合的功能

1.增加功能
boolean add(E e)
boolean addAll(Collection<? extends E> c)
2.删除功能
void clear()
boolean remove(Object o)
boolean removeAll(Collection<?> c)
3.修改功能
Iterator iterator()
Object[] toArray()
4.查询功能
Iterator iterator()
Object[] toArray()
 T[] toArray(T[] a)
5.获取功能
int size()
6.判断功能
boolean contains(Object o)
boolean containsAll(Collection<?> c)
boolean isEmpty()
7.其他功能
boolean retainAll(Collection<?> c)
返回原集合是否发生改变
改变了返回true
没改变返回false
 

public class CollectionDemo01 {
	public static void main(String[] args) {
		Collection c = new ArrayList();
		c.add("张三");
		c.add("李四");
		c.add("王五");
		c.add("赵六");
		System.out.println(c); // [张三, 李四, 王五, 赵六]
		
		// boolean addAll(Collection c)
		Collection c2 = new ArrayList(); 
		c2.add("曹操");
		c2.add("萨达姆");
		c2.add("本拉登");
		c.addAll(c2);
		
		System.out.println(c);
		
		// boolean remove(Object o) 
		System.out.println("remove: " + c.remove("张三"));
		System.out.println(c);
		// boolean removeAll(Collection<?> c) 
//		System.out.println("removeAll: " + c.removeAll(c2));
//		System.out.println(c);
		
		// void clear() 
//		c.clear();
//		System.out.println(c);
		
		System.out.println(c.size());
		
		System.out.println("contains: " + c.contains("李四"));
		System.out.println("contains: " + c.contains("曹操"));
		System.out.println("contains: " + c.contains(""));
		System.out.println("containsAll: " + c.containsAll(c2));
		System.out.println("isEmpty: " + c.isEmpty());
		
		Collection c3 = new ArrayList();
//		c3.add("张三");
//		c3.add("李四");
//		c3.add("王五");
//		c3.add("赵六");
//		c3.add("曹操");
//		c3.add("萨达姆");
//		c3.add("本拉登");
//		c3.add("秦始皇");
		System.out.println(c);
		System.out.println(c3);
		System.out.println("retainAll:" + c.retainAll(c3));
		System.out.println("c:" + c);
		
	}

}

4 集合的遍历

1 iterator 迭代器

查询功能
Iterator<E> iterator() 
 	Object[] toArray() 
遍历 	
 	while (it.hasNext()) {
		Object oj = it.next();
		System.out.println(oj);
	}
public class CollectionDemo02 {
	public static void main(String[] args) {
		// Object[] toArray() 
		Collection c = new ArrayList();
		c.add("希特勒");
		c.add("杨贵妃");
		c.add("貂蝉");
		c.add("赛西施");
//		c.add(c);
//		c.add(100);
		System.out.println(c);
//		Object[] objs = c.toArray();
//		for (Object oj : objs) {
//			String s = (String) oj;
//			System.out.println(s);
//		}
		
//		Iterator<E> iterator() 
//		获取迭代器对象
		Iterator it = c.iterator();
		
//		Object oj = it.next();
//		System.out.println(oj);
//		
//		oj = it.next();
//		System.out.println(oj);
//		
//		oj = it.next();
//		System.out.println(oj);
//		
//		oj = it.next();
//		System.out.println(oj);
//		
//		oj = it.next();
//		System.out.println(oj);
		
		while (it.hasNext()) {
			Object oj = it.next();
			System.out.println(oj);
		}
		
	}
}

2 并发修改异常

java.util.ConcurrentModificationException
异常名称:

修改并发异常

产生原因:

表示在使用迭代器的同时,使用原集合修改了元素

解决办法:

		1.只操作原集合,使用原集合修改
			toArray
			普通for  如下
			Object[] objs = c.toArray();
			for (Object oj : objs) {
			String s = (String) oj;
		
		2.只操作迭代器修改
			使用ListIterator

注意:

foreach遍历集合底层也是使用了迭代器不能够解决并发修改异常

public class CollectionDemo03 {
   public static void main(String[] args) {
   	Collection c = new ArrayList();
   	c.add("希特勒");
   	c.add("杨贵妃");
   	c.add("貂蝉");
   	c.add("赛西施");
   	
//		Iterator it = c.iterator();
//		while (it.hasNext()) {
//			Object oj = it.next();
//			String s = (String) oj;
//			if (s.equals("杨贵妃")) {
//				c.remove("杨贵妃");
//			}
//		}
//		Object[] objs = c.toArray();
//		for (Object oj : objs) {
//			String s = (String) oj;
//			if (s.equals("杨贵妃")) {
//				c.remove("杨贵妃");
//			}
//		}
   	
//		for (Object oj : c) {
//			String s = (String) oj;
//			if (s.equals("杨贵妃")) {
//				c.remove("杨贵妃");
//			}
//		}
   	
//		for (Iterator iterator = c.iterator(); iterator.hasNext();)
//		{
//			Object oj = iterator.next();
//			String s = (String)oj;
//			if (s.equals("杨贵妃"))
//				c.remove("杨贵妃");
//		}
   	
//		for(Iterator iterator = c.iterator();iterator.hasNext();) System.out.println(iterator.next());
   		
   }
}

3 练习

使用集合存储学生对象,并且去除重复学生, 学生编号相同即为同一个学生

使用集合存储员工对象 Employee
1.要求员工对象不能够重复
2.员工的编号和姓名相同认为是同一个员工
3.使用至少三种方式遍历集合输出员工的信息
4.如果员工中存在 隔壁老王,就删除他

public class CollectionDemo04 {
	public static void main(String[] args) {
		// 1.创建容器对象
		Collection c = new ArrayList();
		// 2.创建学生元素对象
		Student s1 = new Student("1001", "张三", 18);
		Student s2 = new Student("1002", "李四", 18);
		Student s3 = new Student("1003", "王五", 18);
		Student s4 = new Student("1003", "王五丰", 18);
		Student s5 = new Student("1004", "赵六", 18);
		Student s6 = new Student("1005", "孙七", 18);
		// 3.将学生存储到集合中
		c.add(s1);
		c.add(s2);
		c.add(s3);
		c.add(s4);
		c.add(s5);
		c.add(s6);
		c.add("hello");
		c.add(100);
		
		// 遍历集合
		Iterator it = c.iterator();
		while (it.hasNext()) {
			Object oj = it.next();
			if (oj instanceof Student) {
				Student s = (Student) oj;
				System.out.println(s);
			} else if (oj instanceof String) {
				String s = (String) oj;
				System.out.println(s);
			}
			
//			System.out.println(((Student)it.next()).getName() + "|" + ((Student)it.next()).getAge());
		}
		
		// 去除重复元素
		// 1.创建一个新的集合
//		Collection c2 = new ArrayList();
//		// 2.遍历旧集合
//		for (Object oj : c) {
//			// 3.判断新集合中是否存在这个元素
//			if (!c2.contains(oj)) {
//				// 4.如果新集合中不存在该元素就存储到集合中
//				c2.add(oj);
//			}
//		}
//		System.out.println("------------------");
//		// 地址传递
//		c = c2;
//		System.out.println("------------------");
//		for (Object object : c) {
//			System.out.println(object);
//		}
//		
	}
}

class Student {
	private String id;
	private String name;
	private Integer age;
	public Student() {
		super();
	}
	public Student(String id, String name, Integer age) {
		super();
		this.id = id;
		this.name = name;
		this.age = age;
	}
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Integer getAge() {
		return age;
	}
	public void setAge(Integer age) {
		this.age = age;
	}
	@Override
	public String toString() {
		return "Student [id=" + id + ", name=" + name + ", age=" + age + "]";
	}
	
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Student other = (Student) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		return true;
	}
}

二 泛型

1 泛型的引入

问题一: 安全隐患问题,如果集合存储的是任意类型,那么我们需要对Object的所有子类做判断,显然安全隐患永远存在
问题一: 就算我们判断了类型,如果新增一个不同类型的元素,那么我们不能遍历完全

没有直接解决方案
模仿数组和方法

1.数组在编译的时候就必须确定类型,如果确定了类型再存储不同类型会编译报错,不存在类型转换问题
2.参数化类型

2 泛型的概念:
泛型属于一种独立的技术,泛型是JDK1.5之后引入的新特性,是一种将元素的数据类型在编译的时候  就确定的类型,
同时是一种参数化类型,一旦确定类型,泛型相关的类,接口,方法所有的类型都会被统一
3 泛型的格式
<E,H,T,K,V>
1. <>里面可以是任意的字母,一般泛型类会使用E,泛型方法会使用T
2. 这里只能够定义引用类型,不能够定义基本书类型
3. <>里面既可以定义一个泛型,也可以定义多个泛型
4  泛型的分类
泛型类
泛型接口
泛型方法
5 泛型的好处:
1. 简化了代码
2. 取消了黄色警告线
3. 取消了强制类型转换,提高了程序的效率
4. 提高了程序的安全性
5. 提高了程序的扩展性和可维护性,满足了开闭原则【对扩展开放,对修改关闭】

2 泛型类

把泛型定义在类上
泛型接口或者泛型类在使用的时候必须确定类型
JDK1.5之前没有使用泛型的时候,代码如下:

public class GernericDemo02 {
	public static void main(String[] args) {
		GenericClass gc = new GenericClass();
		gc.setObj("张三");
		
		Object oj = gc.getObj();
		String s = (String) oj;
		System.out.println(s + "|" + s.length());
	}
}

class GenericClass {
	private Object obj;

	public Object getObj() {
		return obj;
	}

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

以上代码存在安全隐患,如果添加如下代码:

gc.setObj(10);

会出现类型转换异常,我们可以使用泛型类来改进,继续看如下代码:

泛型类改进:
public class GernericDemo02 {
	public static void main(String[] args) {
		GenericClass<String> gc = new GenericClass<String>();
		gc.setE("张三");
//		gc.setE(10);
		
		String s = gc.getE();
		System.out.println(s + "|" + s.length());
	}
}

class GenericClass<E> {
	private E e;

	public E getE() {
		return e;
	}

	public void setE(E e) {
		this.e = e;
	}
}

3泛型接口

把泛型定义在接口上

1.泛型接口实现类的方式

2.泛型接口匿名内部类的方式

泛型接口或者泛型类在使用的时候必须确定类型

泛型接口代码如下:

interface GenericInterface<E, T> {
	
	void test(E e);
	
	T add(T t);
}

泛型接口的使用方式有如下三种:

1.实现类确定泛型类型

   // 1.实现类确定泛型类型
   class GenericInterfaceImpl implements GenericInterface<String, Integer> {
   
   	@Override
   	public void test(String e) {
   		System.out.println(e);
   	}
   
   	@Override
   	public Integer add(Integer t) {
   		return t;
   	}
   	
   }

2.实现类不确定泛型,在调用的时候确定泛型

   // 1 实现类不确定泛型
   class GenericInterfaceImpl<E,T> implements GenericInterface<E, T> {
   
   	@Override
   	public void test(E e) {
   		System.out.println(e);
   	}
   
   	@Override
   	public T add(T t) {
   		return t;
   	}
   	
   }
   // 2 在调用的时候确定泛型
   GenericInterface<String, Double> gi = new GenericInterfaceImpl<String, Double>();
   System.out.println(gi.add(2.5));
   gi.test("hello");

3.匿名内部类确定泛型类型

   GenericInterface<String, Boolean> gi = new GenericInterface<String, Boolean>(){
   
       @Override
       public void test(String e) {
           System.out.println(e);
       }
   
       @Override
       public Boolean add(Boolean t) {
           return t;
       }
   
   };

4 泛型方法

泛型方法: 把泛型定义在方法上,泛型方法可以理解为局部泛型,独立于泛型类

泛型方法的特点:

  1. 泛型方法独立于泛型类或者泛型接口
  2. 泛型方法在方法调用的时候确定类型
  3. 一个泛型接口或者泛型类中可以有多个泛型方法
  4. 一个泛型方法也可以定义多个泛型

泛型方法示例代码如下:

public class GenericDemo04 {
	public static void main(String[] args) {
		GenericMethod<String, Integer> gm = new GenericMethod<String, Integer>();
        // 2.泛型方法在方法调用的时候确定类型
		gm.show(20.5);
		
		Character c = gm.test('c');
		System.out.println(c);
		
		gm.method(25, 2.5);
	}
}

class GenericMethod<E,H> {
	
	private E e;
	private H h;
	
	// 1.泛型方法独立于泛型类或者泛型接口
	public <T> void show(T t) {
		System.out.println(t);
	}
	// 3.一个泛型接口或者泛型类中可以有多个泛型方法
	public <K> K test(K K) {
		return K;
	}
	// 4.一个泛型方法也可以定义多个泛型
	public <V, U> void method(V v, U u) {
		System.out.println(v);
		System.out.println(u);
	}
	
	public E getE() {
		return e;
	}
	public void setE(E e) {
		this.e = e;
	}
	public H getH() {
		return h;
	}
	public void setH(H h) {
		this.h = h;
	}
	
}

泛型方法的应用:

大家知道集合Collection中有一个将集合转换成数组的方法,如下所示:

Object[] toArray();

该方法存在安全隐患,代码如下所示:

Collection<String> con = new ArrayList<String>();
con.add("周星驰");
con.add("成龙");
con.add("李连杰");

Object[] objs = con.toArray();
for (Object oj : objs) {
    Integer integer = (Integer) oj;
    System.out.println(integer.intValue());
}

但是其实该方法也存在着另外一个重载的方法,如下所示:

<T> T[] toArray(T[] a);

方法的设计者无法知道使用者会往集合中存储何种数据类型,所以将将确定类型的权限交给调用来确定类型,那么我们就可以考虑使用泛型方法,使用泛型方法改进后,代码如下所示:

Collection<String> con = new ArrayList<String>();
con.add("周星驰");
con.add("成龙");
con.add("李连杰");

// <T> T[] toArray(T[] a); 取消了强制类型转换和安全隐患
String[] strs = con.toArray(new String[] {});
for (String s : strs) {
    System.out.println(s);
}

注:泛型方法在我们写框架的时候应用非常广泛,所以我们有必要掌握它。

5 泛型限定符

概念: 用来限定泛型的符号
泛型限定符
? : 表示泛型类型可以是任意类型
? extends E : 表示泛型类型可以是E或者E的子类
? super E : 表示泛型类型可以是E或者E的父类

public class GenericDemo05 {
	public static void main(String[] args) {
		Collection<?> c = new ArrayList<Object>();
		Collection<?> c2 = new ArrayList<Father>();
		Collection<?> c3 = new ArrayList<Son>();
		Collection<?> c4 = new ArrayList<Daughter>();
		
//		Collection<? extends Father> c5 = new ArrayList<Object>();
		Collection<? extends Father> c6 = new ArrayList<Father>();
		Collection<? extends Father> c7 = new ArrayList<Son>();
		Collection<? extends Father> c8 = new ArrayList<Daughter>();
		
		Collection<? super Father> c9 = new ArrayList<Object>();
		Collection<? super Father> c10 = new ArrayList<Father>();
//		Collection<? super Father> c11 = new ArrayList<Son>();
//		Collection<? super Father> c12 = new ArrayList<Daughter>();

//		boolean addAll(Collection<? extends E> c);
		/*
		 * 1.当一个方法需要传入一个接口的时候,实际上希望传入该接口的实现类或者匿名内部类
		 * 2.当一个方法的泛型是? extends E实际上希望是 E或者E的子类
		 */
		Collection<Father> cc = new ArrayList<Father>();
		cc.addAll(new ArrayList<Son>());
		cc.addAll(new ArrayList<Daughter>());
		cc.addAll(new ArrayList<Father>());
		
	}
}

class Father {}

class Son extends Father {}

class Daughter extends Father {}

6 泛型嵌套

泛型嵌套:泛型中可以包含泛型
有以下常见几种情况:

  1. Collection嵌套Collection集合
  2. Collection嵌套Map集合
  3. Map嵌套Collection集合

使用集合存储如下数据
一个学校有3个班级,每个班级有4个学生
存储学生到集合中同时遍历集合中的每一个学生
存储:由内到外
遍历:由外到内
 

public class GenericDemo06 {
	public static void main(String[] args) {
		Student s1 = new Student("张三", 18);
		Student s2 = new Student("李四", 18);
		Student s3 = new Student("王五", 18);
		Student s4 = new Student("赵六", 18);
		List<Student> list = new ArrayList<Student>();
		list.add(s1);
		list.add(s2);
		list.add(s3);
		list.add(s4);
		
		Student s5 = new Student("张三2", 18);
		Student s6 = new Student("李四2", 18);
		Student s7 = new Student("王五2", 18);
		Student s8 = new Student("赵六2", 18);
		List<Student> list2 = new ArrayList<Student>();
		list2.add(s5);
		list2.add(s6);
		list2.add(s7);
		list2.add(s8);
		
		Student s9 = new Student("张三3", 18);
		Student s10 = new Student("李四3", 18);
		List<Student> list3 = new ArrayList<Student>();
		list3.add(s9);
		list3.add(s10);
		
		List<List<Student>> outList = new ArrayList<List<Student>>();
		outList.add(list);
		outList.add(list2);
		outList.add(list3);
		
		// 遍历: 由外到内
		int index = 1;
		for (List<Student> innerList : outList) {
			System.out.println(index + "班");
			for (Student s : innerList) {
				System.out.println("\t" + s.getName() + "|" + s.getAge());
			}
			index++;
		}
		
	}
}

class Student {
	private String name;
	private Integer age;
	public Student() {
		super();
	}
	public Student(String name, Integer age) {
		super();
		this.name = name;
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Integer getAge() {
		return age;
	}
	public void setAge(Integer age) {
		this.age = age;
	}
	@Override
	public String toString() {
		return "Student [name=" + name + ", age=" + age + "]";
		}	
	}

7 泛型案例

  1. 使用LinkedList模拟栈结构和队列结构 【要求使用泛型】
  2. 使用反射 + 泛型 编写 Map <-> Bean 相互转换的工具类
  3. 使用反射 + 泛型 编写 Map <-> List 相互转换的工具类
  4. 使用 反射 + 泛型 + 注解 实现 数据库的查询 【要求能够查询数据库的单条记录和多条记录】

三 list接口

1 Collection将集合划分为两大类:

  1. List集合
  2. Set集合

List接口的特点:

  1. 有序【存储有序】
  2. 可重复
  3. 可以存储 null值
  4. 部分子集合线程安全,部分不安全 例如 ArrayList 和 Vector
  5. 有索引,针对每个元素能够方便地查询和修改
  6. 判断元素是否重复依赖于equals方法

        a. 如果元素是系统类,不需要重写equals方法
​        b. 如果是自定义类,就需要我们按需求重写 equals方法
       方法:
       添加:

 void add(int index, E element)  //在指定 index 索引处理插入元素 element
 boolean addAll(int index, Collection<? extends E> c)  // 在指定 index 索引处理插入集合元素 c

删除

E remove(int index) //删除指定索引 index 处的元素

修改

E set(int index, E element) // 修改指定索引 index 处的元素为 element 

获取

E get(int index) 获取指定索引处的元素
int indexOf(Object o) 从左往右查找,获取指定元素在集合中的索引,如果元素不存在返回 -1 
int lastIndexOf(Object o) 从右往左查找,获取指定元素在集合中的索引,如果元素不存在返回 -1
List<E> subList(int fromIndex, int toIndex) 截取从 fromIndex 开始到 toIndex-1 处的元素

遍历

E get(int index) + int size() for循环遍历集合中的每一个元素
ListIterator<E> listIterator() 通过列表迭代器遍历集合中的每一个元素
ListIterator<E> listIterator(int index) 通过列表迭代器从指定索引处开始正向或者逆向遍历集合中的元素

public class ListDemo01 {
   public static void main(String[] args) {
   	List list = new ArrayList();
   	list.add("疯狂的外星人");
   	list.add("流浪地球");
   	list.add("惊奇队长");
   	list.add("新喜剧之王");
   	System.out.println(list);
   	
   	list.add(1, "绿皮书");
   	System.out.println(list);
   	
   	Object oj = list.remove(1);
   	System.out.println(oj);
   	
   	System.out.println(list);
   	
   	list.set(3, "新喜剧之王2");
   	System.out.println(list);
   	
   	System.out.println(list.get(0));
   	
   	/*
   	 * int indexOf(Object o)  
   	 *  int lastIndexOf(Object o)  
   	 *  List<E> subList(int fromIndex, int toIndex) 
   	 */
   	System.out.println(list.indexOf("流浪地球1"));
   	System.out.println(list.lastIndexOf("流浪地球"));
   	List subList = list.subList(1, 3);
   	System.out.println(list);
   	System.out.println(subList);
   }
}

2 List接口的遍历

1 List接口的遍历方式

  1. toArray
  2. Iterator
  3. foreach
  4. 普通for
  5. ListIterator

2 List接口去除重复元素

​ 方式一:创建一个新的集合去除重复元素再使用地址传递

​ 方式二:在原集合的基础上使用选择排序思想去除重复元素

List<String> list = new ArrayList<String>();
		list.add("张三");
		list.add("李四");
		list.add("李四");
		list.add("李四");
		list.add("王五");

for (int i = 0; i < list.size(); i++) {
    for (int j = i + 1; j < list.size(); j++) {
        if (list.get(i).equals(list.get(j))) {
            list.remove(j);
            j--;
        }
    }
}

3 并发修改异常的处理:

异常名称:并发修改异常 java.util.ConcurrentModificationException
产生原因:在使用迭代器迭代的同时使用原集合对元素做了修改
解决办法:

  1. 使用 toArray 方法
  2. 使用 普通 for 遍历
  3. 使用 ListIterator 遍历集合并且使用 列表迭代器修改元素

3 ArrayList

1 ArrayList

概述

List 接口的大小可变数组的实现。实现了所有可选列表操作,并允许包括 null 在内的所有元素。除了实现
List 接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小。(此类大致上等同于 Vector类,除了此类是不同步的。)

特点:

  1. 底层数据结构是数组
  2. 增加和删除的效率低,查询和修改的效率高
  3. 能够存储 null 值
  4. 线程不安全,效率高 可以通过 Collections.synchronizedList();变安全
  5. 有索引,能够方便检索
  6. 元素可重复,我们自己可以通过 选择排序去重复
  7. 不可以排序,但是可以通过 Collections.sort();方法排序

注:ArrayList中常用的方法全部来自于 父类 Collection,List,Object.这里不再做详细叙述。

public class ArrayListDemo {
   public static void main(String[] args) {
   	ArrayList<String> list = new ArrayList<>();
   	list.add("abc");
   	list.add("efg");
   	list.add("hij");
   	
   	for (String string : list) {
   		System.out.println(string);
   	}
   }
}

2 Vector

概述

Vector 类可以实现可增长的对象数组。与数组一样,它包含可以使用整数索引进行访问的组件。但是,Vector 的大小可以根据需要增大或缩小,以适应创建 Vector 后进行添加或移除项的操作。

 

特点

  1. 底层数据结构是数组
  2. 有索引,能够方便检索
  3. 增加和删除的效率低,查询和修改的效率高
  4. 线程安全,效率低
  5. 能够存储 null 值
  6. 元素可重复【我们自己可以通过选择排序思想去除重复元素】
  7. 不可以排序,但是可以通过 Collections.sort();方法排序

常用方法

增加

public synchronized void addElement(E obj) 添加元素 obj 到集合中
public synchronized void insertElementAt(E obj, int index) 在指定索引 index 处插入元素 obj

删除

public synchronized void removeElementAt(int index) 移除指定索引 index 处的元素
public synchronized void removeAllElements() 移除所有元素

修改

public synchronized void setElementAt(E obj, int index) 修改指定索引 index 的元素为 obj

遍历

public synchronized E elementAt(int index) + size() for循环遍历集合中的所有元素
public synchronized Enumeration<E> elements() 使用 Enumeration 迭代器遍历集合中的元素

获取

public synchronized E firstElement() 获取集合中的第一个元素
public synchronized E lastElement() 获取集合中的最后一个元素
public synchronized E elementAt(int index) 获取指定索引 index 的元素

相关面试题

ArrayList和Vector的区别?
1)  Vector的方法都是同步的(Synchronized),是线程安全的(thread-safe),而ArrayList的方法不是,由于线程的同步必然要影响性能,因此,ArrayList的性能比Vector好。 
2) 当Vector或ArrayList中的元素超过它的初始大小时,Vector会将它的容量翻倍,而ArrayList只增加50%的大小,这样,ArrayList就有利于节约内存空间。

ArrayList 和 Vector的区别

public class VectorDemo {
	public static void main(String[] args) {
		Vector<String> v = new Vector<String>();
		v.addElement("abc");
		v.addElement("efg");
		v.addElement("hij");
		
		Enumeration<String> e = v.elements();
		while (e.hasMoreElements()) {
			String element = e.nextElement();
			System.out.println(element);
		}
		
		System.out.println("=================");
		
		for (int i = 0; i < v.size(); i++) {
			String element = v.elementAt(i);
			System.out.println(element);
		}
	}
}

3 Stack

概述

Stack 类表示后进先出(LIFO)的对象堆栈。它通过五个操作对类 Vector 进行了扩展 ,允许将向量视为堆栈。它提供了通常的 push 和 pop 操作,以及取堆栈顶点的 peek 方法、测试堆栈是否为空的 empty 方法、在堆栈中查找项并确定到堆栈顶距离的 search 方法。

 特点:

  1. 基于栈结构的集合,先进后出
  2. Stack 类是 Vector类的子类,所以该类也是线程安全的,效率低,建议使用 Deque接口的实现类

常用方法:
E push(E item) 将元素压入栈底
E pop() 将元素从栈结构中弹出,并作为此函数的值返回该对象,此方法会影响栈结构的大小
E peek() 查看堆栈顶部的对象,但不从栈中移除它。
boolean empty() 测试栈是否为空。
int search(Object o) 返回对象在栈中的位置,以 1 为基数。
注:如果栈中元素为空,再尝试弹栈,将会抛出 EmptyStackException 异常, 而不是 NoSuchElementException

**示例代码

Stack<String> stack = new Stack<>();
// 压栈
stack.push("A");
stack.push("B");
stack.push("C");

while (!stack.isEmpty()) {
    System.out.println("栈顶元素:" + stack.peek());
    // 弹栈
    System.out.println("弹出栈顶元素:" + stack.pop());
}

四 Queue

1 Queue

概述

在处理元素前用于保存元素的 collection。除了基本的 Collection 操作外,队列还提供其他的插入、提取和检查操作。每个方法都存在两种形式:一种抛出异常(操作失败时),另一种返回一个特殊值(null 或
false,具体取决于操作)。插入操作的后一种形式是用于专门为有容量限制的 Queue 实现设计的;在大多数实现中,插入操作不会失败。

常用方法:
1,boolean add(E e)
将指定的元素插入此队列(如果立即可行且不会违反容量限制),在成功时返回 true,如果当前没有可用的空间,则抛出 IllegalStateException。
2, E element()
获取,但是不移除此队列的头。
3, boolean offer(E e)
将指定的元素插入此队列(如果立即可行且不会违反容量限制),当使用有容量限制的队列时,此方法通常要优于 add(E),后者可能无法插入元素,而只是抛出一个异常。
4,E peek()
获取但不移除此队列的头;如果此队列为空,则返回 null。
5, E poll()
获取并移除此队列的头,如果此队列为空,则返回 null。
6,E remove()
获取并移除此队列的头。

public class QueueDemo {
	public static void main(String[] args) {
		Queue<String> queue = new ArrayDeque<>();
		queue.offer("abc");
		queue.offer("efg");
		queue.offer("hij");
		
//		for (String s : queue) {
//			System.out.println(s);
//		}
		
//		String s = queue.poll();
//		System.out.println(s);
//		
//		s = queue.poll();
//		System.out.println(s);
//		
//		s = queue.poll();
//		System.out.println(s);
//		
//		s = queue.poll();
//		System.out.println(s);
		
		while (!queue.isEmpty()) {
			System.out.println(queue.poll());
		}
		
	}
}

2双端列操作(Deque)

具备了 Queue 接口的方法 如下

抛出异常                         返回特殊值 
插入 add(e)                     offer(e) 
移除 remove()                    poll() 
检查 element()                   peek() 

同时有自己的双端队列方法

双向队列操作
插入元素

  • addFirst(): 向队头插入元素,如果元素为null,则发生空指针异常
  • addLast(): 向队尾插入元素,如果为空,则发生空指针异常
  • offerFirst(): 向队头插入元素,如果插入成功返回true,否则返回false
  • offerLast(): 向队尾插入元素,如果插入成功返回true,否则返回false

移除元素

  • removeFirst(): 返回并移除队头元素,如果该元素是null,则发生NoSuchElementException
  • removeLast(): 返回并移除队尾元素,如果该元素是null,则发生NoSuchElementException
  • pollFirst(): 返回并移除队头元素,如果队列无元素,则返回null
  • pollLast(): 返回并移除队尾元素,如果队列无元素,则返回null

获取元素

  • getFirst(): 获取队头元素但不移除,如果队列无元素,则发生NoSuchElementException
  • getLast(): 获取队尾元素但不移除,如果队列无元素,则发生NoSuchElementException
  • peekFirst(): 获取队头元素但不移除,如果队列无元素,则返回null
  • peekLast(): 获取队尾元素但不移除,如果队列无元素,则返回null

栈操作
pop(): 弹出栈中元素,也就是返回并移除队头元素,等价于removeFirst(),如果队列无元素,则发生NoSuchElementException
push(): 向栈中压入元素,也就是向队头增加元素,等价于addFirst(),如果元素为null,则发生NoSuchElementException,如果栈空间受到限制,则发生IllegalStateException
引用场景

  1. 满足FIFO场景时
  2. 满足LIFO场景时,曾经在解析XML按标签时使用过栈这种数据结构,但是却选择Stack类,如果在进行栈选型时,更推荐使用Deque类,应为Stack是线程同步

同时还有Stack结构的方法

push(e) addFirst(e)

pop() removeFirst()

peek() peekFirst()

 public class DequeDemo {
	public static void main(String[] args) {
		Deque<String> deque = new ArrayDeque<>();
//		deque.addFirst("Hello");
//		deque.addFirst("World");
//		deque.addFirst("Java");
		
//		deque.addLast("Hello");
//		deque.addLast("World");
//		deque.addLast("Java");
//		
//		System.out.println(deque);
		
		deque.push("A");
		deque.push("B");
		deque.push("C");
		
		while (!deque.isEmpty()) {
			System.out.println(deque.pop());
		}
	}
}

3 ArrayDeque

概述

Deque 接口的大小可变数组的实现。数组双端队列没有容量限制;它们可根据需要增加以支持使用。它们不是线程安全的;在没有外部同步时,它们不支持多个线程的并发访问。禁止 null 元素。此类很可能在用作堆栈时快于 Stack,在用作队列时快于 LinkedList

特点:

  1. 基于数组实现的双端队列
  2. 数组双端队列没有容量限制
  3. 线程不安全效率高
  4. 此类很可能在用作堆栈时快于 Stack,在用作队列时快于 LinkedList。

4 LinkedList

LinkedList类

  1. 基于链表实现的双端队列实现
  2. 数组双端队列没有容量限制
  3. 线程不安全效率高
  4. 如果增加和删除操作比较多,可以优先选择此类,如果查询和修改操作比较多,可以使用ArrayDeque

使用LinkedList模拟栈结构和队列结构
后期发现 TreeSet底层就是TreeMap的实现

五 Set接口

1 Set接口的特点:

  1. 无序
  2. 唯一
  3. 可以存储null值,但是null不能重复
public class SetDemo01 {
   public static void main(String[] args) {
   	Set<String> set = new HashSet<>();
   	
   	set.add("ab");
   	set.add("ac");
   	set.add("ba");
   	set.add("bc");
   	
   	System.out.println(set);
   }
}

2 HashSet

1 HashSet底层数据结构是哈希表
1.为什么hashset能够保证元素唯一? – 依赖于 hashCode 和 equals 方法
2.为什么是无序的? – 因为 对象的 hashCode的值和哈希表存储的索引有关,hashCode是相对随机的,所以是无序的
3.为什么除了第一次运行,每次结果都是一样的?
需要了解 HashSet集合是如何存储?
集合存储方式依赖于 数据结构
**HashMap **
哈希算法
1.哈希算法和对象本身有关
2.哈希算法返回的是一个整数值
3.哈希算法和传入的对象本身的hashCode有关
4.哈希算法返回的整数值就是作为哈希表的索引存储到对应的位置
 

public class HashSetDemo01 {
	public static void main(String[] args) {
		HashSet<String> hs = new HashSet<>();
		hs.add("张三");
		hs.add("李四");
		hs.add("李四");
		hs.add("王五");
		
		System.out.println(hs);
		
	}
}

**2 LinkedHashSet **
底层数据结构是链表 + 哈希表
链表保证元素有序
哈希表保证元素唯一

public class LinkedHashSetDemo {
	public static void main(String[] args) {
		LinkedHashSet<String> lhs = new LinkedHashSet<>();
		lhs.add("AFAF");
		lhs.add("BDAD");
		lhs.add("CDAD");
		lhs.add("DFDS");
		lhs.add("DFDS");
		lhs.add("QE");
		for (String s : lhs) {
			System.out.println(s);
		}
	}
}

3 TreeSet

TreeSet特点
1.无序
2.唯一
3.可排序
4.线程不安全的,效率高
5.可以存储null值
6.底层数据结构是二叉树

a.为什么二叉树结构能够去除重复元素?
b.如何保证元素可排序?
1.TreeSet是如何保证元素唯一?
2.TreeSet是如何保证元素可排序的?

public class TreeSetDemo01 {
	public static void main(String[] args) {
		TreeSet<Integer> ts = new TreeSet<>();
		ts.add(40);
		ts.add(38);
		ts.add(43);
		ts.add(42);
		ts.add(37);
		ts.add(44);
		ts.add(39);
		ts.add(38);
		ts.add(44);
		
		for (Integer i : ts) {
			System.out.println(i);
		}
	}
}

排序
1.TreeSet的排序方式有两种: 自然排序和比较器排序
2.具体使用哪种排序方式,取决于构造方法,无参构造方法就是自然排序,带Comparator接口的构造方法就是比较器排序
3.如果同时使用自然排序和比较器排序,比较器排序优先

public class TreeSetDemo03 {
	public static void main(String[] args) {
//		TreeSet<Student> ts = new TreeSet<Student>();
//		TreeSet<Student> ts = new TreeSet<>(new MyComparatorImpl());
//		匿名内部类的方式
		TreeSet<Student> ts = new TreeSet<>(new Comparator<Student>() {
			@Override
			public int compare(Student s1, Student s2) {
				System.out.println("我是比较器排序");
				Collator c = Collator.getInstance(Locale.CHINESE);
				// 年龄相等,按照姓名排序
				int cmp = s1.getAge() - s2.getAge();
				int cmp2 = (cmp == 0) ? c.compare(s1.getName(), s2.getName()) : cmp;
				return cmp2;
			}
		});
		ts.add(new Student("张三", 18));
		ts.add(new Student("张三", 18));
		ts.add(new Student("李四", 20));
		ts.add(new Student("王五", 22));
		ts.add(new Student("赵六", 18));
		ts.add(new Student("孙七", 18));
		ts.add(new Student("孙七", 18));
		ts.add(new Student("孙哈", 18));
		ts.add(new Student("钱八", 19));
		ts.add(new Student("周九", 18));
		ts.add(new Student("周拔", 18));
		ts.add(new Student("周啊", 18));
		ts.add(new Student("刘十", 18));
		ts.add(new Student("双十一", 15));
		
		for (Student student : ts) {
			System.out.println(student);
		}
	}
}

class MyComparatorImpl implements Comparator<Student> {

	@Override
	public int compare(Student s1, Student s2) {
		Collator c = Collator.getInstance(Locale.CHINESE);
		// 年龄相等,按照姓名排序
		int cmp = s1.getAge() - s2.getAge();
		int cmp2 = (cmp == 0) ? c.compare(s1.getName(), s2.getName()) : cmp;
		return cmp2;
	}
}

六 map

collection 单列集合

1 map 双列集合

1 Map的特点:

  1. Map设计为一个接口
  2. Map针对的是键和值有一定的映射关系
  3. 键应该是唯一的,无序的,类似于Set接口
  4. 值应该是可重复,类似于Collection
  5. Map也应该具备集合应该有的方法
  6. 值的顺序取决于键的顺序,map的数据结构完全取决于键的数据结构,与值无关

2 map方法

添加
V put(K key, V value) 
void putAll(Map<? extends K,? extends V> m) 
删除
V remove(Object key) 
void clear() 
修改
V put(K key, V value)
遍历
get(Object key)
Set<K> keySet() 
Set<Map.Entry<K,V>> entrySet() 
判断
boolean containsKey(Object key) 
boolean containsValue(Object value) 
boolean isEmpty() 
获取
get(Object key)
Set<K> keySet()
Collection<V> values() 
int size() 
static interface Map.Entry<K,V> 
K getKey();
V getValue();
V setValue(V value);

示例如下

public class MapDemo01 {
   public static void main(String[] args) {
   	Map<String, String> map = new HashMap<String, String>();
   	map.put("文章", "姚笛");
   	map.put("文章", "马伊琍");
   	map.put("陈羽凡", "白百何");
   	map.put("王宝强", "马蓉");
   	map.put("汪峰", "章子怡");
//		System.out.println(map);
//		System.out.println("put:" + map.put("文章", "姚笛"));// null
//		System.out.println("put:" + map.put("文章", "马伊琍"));// 姚笛
   	System.out.println(map);
   	
   	System.out.println("remove:" + map.remove("文章"));
   	System.out.println(map);
   	
//		map.clear();
   	System.out.println(map);
   	
   	System.out.println(map.size());
   	
   	System.out.println(map.isEmpty());
   	
   	/*
   	 * boolean containsKey(Object key) 
   	 * boolean containsValue(Object value) 
   	 */
   	System.out.println("containsKey:" + map.containsKey("汪峰"));
   	System.out.println("containsValue:" + map.containsValue("章子怡1"));
   	/*
   	 *  get(Object key)
   	 *  Set<K> keySet()
   	 *  Collection<V> values() 
   	 *  int size() 
   	 */
   	System.out.println("get: " + map.get("汪峰1"));
   	
   	// 获取键的集合
   	Set<String> keys = map.keySet();
   	for (String key : keys) {
   		System.out.println(key);
   	}
   	
   	// Collection<V> values()
   	Collection<String> values = map.values();
   	for (String value : values) {
   		System.out.println(value);
   	}
   	
   }
}

3 map 的遍历方式

Map的遍历方式
方式1:根据Key查找Value Set set = map.keySet()

获取所有Key的集合

遍历Key的集合,获取到每一个Key

根据Key查找Value

示例如下

public class MapDemo02 {
	public static void main(String[] args) {
		Map<String, String> map = new HashMap<String, String>();
		map.put("文章", "姚笛");
		map.put("文章", "马伊琍");
		map.put("陈羽凡", "白百何");
		map.put("王宝强", "马蓉");
		map.put("汪峰", "章子怡");
//		// 获取键的集合
//		Set<String> keys = map.keySet();
//		// 遍历键的集合获取到每一个键
//		for (String key : keys) {
//			// 通过键找值
//			String value = map.get(key);
//			System.out.println(key + "=" + value);
//		}
		
		// 获取键的集合
		// 遍历键的集合获取到每一个键
		for (String key : map.keySet()) {
			// 通过键找值
			System.out.println(key + "=" + map.get(key));
		}
		
		System.out.println("=====================");
		// 方式二: 通过键值对分别找键找值
		// Set<Map.Entry<K,V>> entrySet() 
//		Set<Entry<String, String>> keyValues = map.entrySet();
		for (Entry<String, String> keyValue : map.entrySet()) {
			System.out.println(keyValue.getKey() + "=" + keyValue.getValue());
		}
		
		System.out.println("=====================");
		//方式三
		Set<String> keys = map.keySet();
		Iterator<String> it = keys.iterator();
		while (it.hasNext()) {
			String key = it.next();
			String value = map.get(key);
			System.out.println(key + "=" + value);
		}
		
		System.out.println("=====================");
		//方式四
		Set<Entry<String, String>> keyValues = map.entrySet();
		Iterator<Entry<String, String>> iterator = keyValues.iterator();
		while (iterator.hasNext()) {
			Entry<String, String> keyValue = iterator.next();
			System.out.println(keyValue.getKey() + "=" + keyValue.getValue());
		}
		
		System.out.println("=====================");
		System.out.println(map);
		
	}
}

2 HashMap

HashMap 底层数据结构仅对键有效,数据结构是哈希表
哈希表原理:
如何保证无序?
依赖的是哈希算法,因为哈希算法返回的整数值和对象本身有关,
也和对象本身的hashCode有关,所以这个哈希值作为索引存储在哈希表就是随机无序的
如何保证唯?
首先判断两个对象hashCode是否相等
相等
判断两个对象的equals方法是否相等
相等
重复元素,不存储到哈希表中
不相等
存储到哈希表中
不相等
不存储到哈希表中
示例如下
 

/*
 *	去重复元素
 * 学生id			姓名		年龄 		成绩
 * 2018050401	张三		18		80.0
 * 2018050402	李四		20		85.0
 * 2018050403	李四		21		89.0
 * 2018050404	王五		21		89.0
 * Set<Student>
 * Map<String, Student>
 */
public class HashMapDemo01 {
	public static void main(String[] args) {
		HashMap<String, Student> hm = new HashMap<>();
		hm.put("2018050401", new Student("2018050401", "张三", 18, 80.0));
		hm.put("2018050402", new Student("2018050402", "李四", 20, 85.0));
		hm.put("2018050402", new Student("2018050402", "李四", 20, 85.0));
		hm.put("2018050403", new Student("2018050403", "李四", 21, 89.0));
		hm.put("2018050404", new Student("2018050404", "王五", 21, 89.0));
		
		Set<String> keys = hm.keySet();
		for (String key : keys) {
			Student s = hm.get(key);
			System.out.println(key + "=" + s);
		}		
	}
}

3 TreeMap

基于红黑树(Red-Black tree)的 NavigableMap 实现。
该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。

特点:
键可排序,唯一,
值可重复
底层数据结构是自平衡的二叉树,可排序
排序方式类似于TreeSet,分为自然排序和比较器排序,具体取决于使用的构造方法

示例如下

public class TreeMapDemo02 {
	public static void main(String[] args) {
		TreeMap<Student, String> tm = new TreeMap<>(new Comparator<Student>() {

			@Override
			public int compare(Student s1, Student s2) {
				Collator c = Collator.getInstance(Locale.CHINESE);
				// 学号 姓名 年龄 成绩
				int cmp = s1.getId().compareTo(s2.getId());
				int cmp2 = (cmp == 0) ? c.compare(s1.getName(), s2.getName()): cmp;
				int cmp3 = (cmp2 == 0) ? s1.getAge() - s2.getAge() : cmp2;
				double cmp4 = (cmp3 == 0) ? s1.getScore() - s2.getScore() : cmp3;
				return cmp4 > 0 ? 1 : (cmp4 == 0 ? 0: -1);
			}
		});
		tm.put(new Student("1001", "隔壁老王1", 30, 80.0), "哈哈2");
		tm.put(new Student("1002", "隔壁老王2", 60, 80.0), "哈哈4");
		tm.put(new Student("1003", "隔壁老王3", 30, 780.0), "哈哈4235");
		tm.put(new Student("1009", "隔壁老王4", 30, 50.0), "哈哈6");
		tm.put(new Student("1009", "隔壁老王4", 30, 50.0), "哈哈6");
		tm.put(new Student("1009", "隔壁老王", 30, 40.0), "哈哈6");
		tm.put(new Student("1009", "隔壁老王", 30, 50.0), "哈哈6");
				
		Set<Entry<Student, String>> keyValues = tm.entrySet();
		for (Entry<Student, String> keyValue : keyValues) {
			Student stu = keyValue.getKey();
			String s = keyValue.getValue();
			System.out.println(stu + "=" + s);
		}		
	}
}

4 Hashtable

底层数据结构也是哈希表和HashMap是完全兼容的
但是 Hashtable是线程安全的,效率低,不可以存储null值,null键

public class HashtableDemo {
	public static void main(String[] args) {
		Hashtable<String, String> table = new Hashtable<>();
		table.put("hello", "231");
		table.put("world", "231");
		table.put("java", "231");
		table.put("hello", "231");
		table.put("hello", "231");
//		table.put(null, "313");
		table.put("32131", null);
		
		System.out.println(table);
		
	}
}

5 LinkedHashMap

Map 接口的哈希表和链接列表实现,具有可预知的迭代顺序
特点:
键有序,唯一,
值有序,可重复,类似于List
底层数据结构是哈希表和链表,哈希表保证键唯一,链表保证键有序

public class LinkedHashMapDemo01 {
	public static void main(String[] args) {
		LinkedHashMap<String, String> lhm = new LinkedHashMap<>();
		lhm.put("hello", "dasdada");
		lhm.put("world", "dasdada");
		lhm.put("java", "dasdada");
		lhm.put("andorid", "dasdada");
		lhm.put("andorid", "dasdada");
		lhm.put("andorid", "dasdada");
		System.out.println(lhm);
	}
}

6 WeakHashMap

WeakHashMap类讲解
示例演示

public class WeakHashMapDemo {
	public static void main(String[] args) {
		WeakHashMap<String, String> whm = new WeakHashMap<>();
//		whm.put("hello", "12313");
//		whm.put("world", "12313");
//		whm.put("java", "12313");
//		whm.put("hello", "12313");
		
		whm.put(new String("hello"), "12313");
		whm.put(new String("world"), "12313");
		whm.put(new String("java"), "12313");
		whm.put(new String("hello"), "12313");
		whm.put("heihei", "12313");
		
		System.gc();
		System.runFinalization();
//		try {
//			Thread.sleep(3000);
//		} catch (InterruptedException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
		System.out.println(whm);
	}
}

七 Enum(枚举)

1 Enum 的概念

枚举是在一定范围内取值,并且这个值必须是枚举类型中的任意一个,并且只能有一个

枚举的本质就是一个Java类
枚举是一个类,那么枚举就有构造方法,成员方法,静态方法,静态变量,成员变量,抽象方法,
但是除了抽象方法,其他的方法都没有意义。

2 Enum的特点

1.必须在规定范围内取值
2.这个值只能取一个
3.这个值可以是规定范围内的任意一个
4.枚举中所有的成员,必须出现在枚举对象的下面
5.如果枚举类中有一个成员,那么 枚举对象最后不能省略分号
6.枚举中构造方法必须私有
7抽象方法有意义 - 可以用来描述某个枚举成员的信息,提高程序的可读性
8.枚举也是switch语句中 的常量 形式之一
switch语句中可以有哪些?
byte short int char String 枚举

// Java设计了更简便的方式 枚举
enum Game4 {
START, OVER, RUNNING
}

public class EnumDemo02 {
	public static void main(String[] args) {
		Color c = Color.GREEN;
//		c.show();
//		Color.RED.desc();

		switch (c) {
		case RED:
			Color.RED.desc();
			break;
		case GREEN:
			Color.GREEN.desc();
			break;
		case YELLOW:
			Color.YELLOW.desc();
			break;

		default:
			break;
		}
	}
}

enum Color {
//抽象方法可以用来描述某个枚举成员的信息
//匿名内部类
	RED () {

		@Override
		public void desc() {
			System.out.println("我是红色");
		}

	},
	GREEN() {

		@Override
		public void desc() {
			System.out.println("我是绿色");
		}

	},
	YELLOW() {
		@Override
		public void desc() {
			System.out.println("我是黄色");
		}
	};
	public int num;
	public final static int A = 10;

	private Color() {
	}

	private Color(int num) {
		this.num = num;
	}

	public void show() {
		System.out.println("show");
	}

	public abstract void desc();
}

 3 Enum常用方法

枚举中常用的方法
String name()
返回此枚举常量的名称,与其枚举声明中声明的完全相同。
int ordinal()
返回此枚举常数的序数(其枚举声明中的位置,其中初始常数的序数为零)。
static <T extends Enum>
T valueOf(类 enumType, String name)
返回具有指定名称的指定枚举类型的枚举常量。
T valueOf(String name)
T[] values();

需求: 实现枚举对象 、 枚举下标、 枚举名称之间的相互转换

public class EnumDemo03 {
	public static void main(String[] args) {
//		method01(TrafficLight.GREEN);
//		method02("GREEN");
//		method03(2);
		
		TrafficLight t = TrafficLight.valueOf(TrafficLight.class, "GREEN");
		System.out.println(t);
	}

	/**
	 * 已知枚举的下标,获取枚举的名称,枚举的对象
	 */
	public static void method03(int index) {
		// values方法返回枚举数组
		TrafficLight trafficLight = TrafficLight.values()[index];
		System.out.println(trafficLight);
		String name = trafficLight.name();
		System.out.println(name);
	}

	/**
	 * 已知枚举的名称,获取枚举的下标和枚举对象
	 */
	public static void method02(String name) {
		// T valueOf(String name)
		TrafficLight trafficLight = TrafficLight.valueOf(name);
		int index = trafficLight.ordinal();
		System.out.println(trafficLight + ":" + index);
	}

	/**
	 * 已知枚举对象,获取枚举的下标和名称
	 */
	public static void method01(Enum<TrafficLight> e) {
		System.out.println(e.ordinal() + ":" + e.name());
	}
}

enum TrafficLight{
	RED, GREEN, YELLOW
}

4 Enum 练习

package com.root.enumdemo;

/*
 * 使用枚举来描述一个星期多少天
 * 并且要使用switch测试
 * 要求枚举中用抽象方法来描述每一天
 */
public enum Weekend {

	MONDAY {
		@Override
		void desc() {
			System.out.println("星期一");
		}
	},
	TUESDAY {
		@Override
		void desc() {
			System.out.println("星期二");
		}
	},
	WEDNESDAY {
		@Override
		void desc() {
			System.out.println("星期三");
		}
	},
	THURSDAY {
		@Override
		void desc() {
			System.out.println("星期四");
		}
	},
	FRIDAY {
		@Override
		void desc() {
			System.out.println("星期五");
		}
	},
	SATURDAY {
		@Override
		void desc() {
			System.out.println("星期六");
		}
	},
	SUNDAY {
		@Override
		void desc() {
			System.out.println("星期天");
		}
	};

	abstract void desc();

	@Deprecated
	public static void main(String[] args) {
		Weekend day = Weekend.SATURDAY;
		switch (day) {
		case MONDAY:
			Weekend.MONDAY.desc();
			break;
		case TUESDAY:
			Weekend.TUESDAY.desc();
			break;
		case WEDNESDAY:
			Weekend.WEDNESDAY.desc();
			break;
		case THURSDAY:
			Weekend.THURSDAY.desc();
			break;
		case FRIDAY:
			Weekend.FRIDAY.desc();
			break;
		case SATURDAY:
			Weekend.SATURDAY.desc();
			break;
		case SUNDAY:
			Weekend.SUNDAY.desc();
			break;

		default:
			System.out.println("非法星期");
			break;
		}

	}
}

 

 

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值