黑马程序员-day14-集合框架(Colection)

------- android培训java培训、期待与您交流! ----------

集合类
思考:
1.为什么出现集合类?
面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储,集合就是存储对象最常用的一种方式。
2.数组和集合类同是容器,有何不同?
数组虽然也可以存储对象,但长度是固定的;集合长度是可变的。数组中可以存储基本数据类型,集合只能存储对象。

集合框架的构成及分类



集合概述:
Java是面向对象语言,如果我们要针对多个对象进行操作,就必须对多个对象进行存储。
而对多个元素进行存储,前面我们学习过数组,数组的弊端,长度固定。这样,数组将不能满足变化的要求。所以,Java就提供了集合供我们使用。

集合的特点:
1.长度可以发生改变。
2.只能存储对象。
3.可以存储多种类型对象(一般存储的还是同一种,因为1.5JDK的新特性 泛型)。

集合和数组的区别:
1.长度
  数组固定。
  集合可变。
2.存储元素
  数组可以是基本类型,也可以是引用类型。
  集合只能是引用类型。(JDK1.5以后还可以存储基本数据类型,因为JDK1.5自动装箱拆箱)
3.储存是否是同一类型
  数组元素类型一致。
  集合元素类型可以不一致。(建议还是同一种类型,因为JDK1.5出现了泛型)
4.功能是否一致
  数组只能对数据进行存取操作,当然删除数组中的元素还可以删除,就是null。
  集合不但可以对数据进行基本操作,还提供了更强大的功能,比如删除 修改...。

集合体系的由来
集合是存储多个元素的容器,但是,由于数据结构不同,Java就提供了多种集合类。
而这多种集合类有共性的功能,所以,通过不断的向上抽取,最终形成了集合体系
结构。

为什么会出现这么多的容器呢?
因为每一个容器对数据的存储方式都有不同,这个存储方式称之为:数据结构。
数据结构:数据存储的方式。

如何学习和使用一个继承体系呢?
学习顶层:因为顶层定义的是共性内容。
使用底层:因为底层才是具体的实现。
简单一句话:参阅顶层内容,建立底层对象。


集合框架中的常用接口
1.Collection接口有两个子接口:
  List(列表) ,Set(集)
2.List和Set的区别?
  List:可存放重复元素,元素存取是有序的。因为该集合体系有索引。
  Set:不可以存放重复元素,元素存取是无序的(存入和取出顺序不一定一致)。

Collection定义了集合框架的共性功能。
1.添加
add(e);
addAll(collection);
2.删除
remove(e);
removeAll(collection);
clear();
3.判断。
contains(e);
isEmpty();
4.获取
iterator();
size();
5.获取交集。
retainAll();
6.集合变数组。
toArray();

Collection共性功能实例
import java.util.List;
import java.util.ArrayList;

public class CollectionDemo{
	public static void main(String[] args){
		ArrayList al = new ArrayList();
		//添加一个元素
		al.add("张三");
		al.add("李四");
		al.add("王五");
		
		sop(al.get(1));//输出一个元素
		al.remove("王五");//删除一个元素
		sop(al.size());//打印下集合的长度 结果为2.因为删了一个
		al.clear();//清空集合
		sop(al.contains("王五"));//判断该列表中是否有这个元素
		sop(al.isEmpty);//判断该列表是否为空
		method();
	}

	//取交集和取交集
	public static void method(){
		List list1 = new ArrayList();
		list1.add("张三");
		list1.add("李四");
		list1.add("王五");
		List list2 = new ArrayList();
		list2.add("张三");
		list2.add("李四");
		list2.add("赵六");
		//list1.retainAll(list2);//取交集。拿list1元素跟list2的元素比,如果相同就保留。调用父类的方法
		//sop(list1);
		list1.removeAll(list2);//删除交集。拿list1的元素跟list2的元素比,如果相同就删除。调用父类的方法
		sop(list1);
	}
	public static void sop(Object obj){
		System.out.println(obj);
	}
}
注意:
1.直接存储基本类型值也是可以的,因为JDK1.5后,有自动装箱技术,会将基本数据类转换成数据对象。JDK1.4绝对不行,因为集合中存放的对象的引用,当直接存放基本类型值的时候,会将这个基本类型值变成一个对象(自动装箱)。
2.集合对象中存储的其实都是元素对象的引用(地址)。
3.add方法的参数是Object类型可以接受所有类型的对象。但会出现向上转型,取出元素时的类型还是Object,不能使用具体对象的特有内容,想要是有特有内容,必须向下转型。集合框架中存放的都是对象的地址。当我们想用对象的特有方法的时候必须向下转型。当我们想直接打印一个集合,其实是调用了这个对象的toString方法,当我们要打印一个对象的长度的时候,首先我们会想到length方法。但是由于我们添加数据的时候用的add方法,而add方法的参数类型是Object,也就说把存进去的数据向上转型了。但是我们都知道Object是没有length方法,此时无法使用length方法。那么就要向下转型才可以,只有使用子类的特有方法size。


List接口中常用的类
List可存放重复元素,元素存取是有序的。因为该集合体系有索引。
1.ArrayList:底层是数组数据结构,查询速度很快,但是增删稍慢,线程不同步,默认10个元素。(线程不同步)
2.LinkedList:底层是链表数据结构,查询很慢,增删速度很快。(线程不同步)
3.Vector:底层是数组数据结构,和ArrayList一样,查询,增删,都很慢,Vector是1.0出现,ArrayList是1.2出现,(线程同步)但已经不用了。

取出List集合中元素的方式:
1.get(int index):通过脚标获取元素。
2.遍历取出:
  Iterator():通过迭代方法获取迭代器对象。
  ListIterator():Iterator()的子类,只能用于List集合。
注意:Iterator里面的方法是有限的,只能对元素进行判断,取出,删除的操作
如果想要其他的操作,如添加、修改等,就需要使用其子接口,ListIterator。

迭代器(Iterator)
1.迭代是取出集合中元素的一种方式。如同抓娃娃游戏机中的夹子。
2.因为Collection中有iterator方法,所以每一个子类集合对象都具备迭代器。
3.用法:
  (1)Iterator iter = l.iterator();
     while(iter.hasNext()){
      System.out.println(iter.next());
     }
  (2)for(Iterator iter = iterator();iter.hasNext();){
      System.out.println(iter.next());
     }

迭代器的使用
1.使用步骤
  (1)通过集合对象获取迭代器对象。
  (2)通过迭代器对象判断。
  (3)通过迭代器对象获取。
2.迭代器原理
  由于多种集合的数据结构不同,所以存储方式不同,所以,取出方式也不同。这个时候,我们就把判断和获取功能定义在了一个接口中,将来,遍历哪种集合的时候,只要该集合内部实现这个接口即可。
3.集合的常见使用步骤
  (1)创建集合对象。
  (2)创建元素对象。
  (3)把元素添加到集合中。
  (4)遍历集合。
     通过集合对象获取迭代器对象。
     通过迭代器对象判断。
     通过迭代器对象获取。

遍历自定义对象实例(Iterator)
import java.util.ArrayList;
import java.util.Iterator;
/*
 * 遍历自定义对象(Iterator)
 */
//学生类
class Student{
	private String name;
	private int age;
	Student(String name, int age){
		this.name = name;
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
}

public class IteratorDemo {
	public static void main(String[] args) {
		ArrayList al = new ArrayList();
		al.add(new Student("张三", 20));
		al.add(new Student("李四", 21));
		al.add(new Student("王五", 22));
		
		for(Iterator it = al.iterator();it.hasNext();){
			Student stu = (Student)it.next();
			
			System.out.println("name:" + stu.getName() + ",age:" + stu.getAge());
		}
	}
}

Iterator迭代注意事项
1.迭代器是取出方式,会直接访问集合中的元素。所以将迭代器通过内部类的形式来进行描述。通过容器的iterator()方法获取该内部类的对象。
2.迭代器在Collcection接口中是通用的,它替代了Vector类中的Enumeration(枚举)。
3.迭代器的next方法是自动向下取元素,要避免出现NoSuchElementException。
4.迭代器的next方法返回值类型是Object,所以要记得类型转换。
5.在迭代时,不可以通过集合对象的方法操作集合中的元素。因为会发生ConcurrentModificationException异常。

思考:为什么next方法的返回类型是Object的呢?
因为add方法的参数是Object类型可以接受所有类型的对象。会出现向上转型,取出元素时的类型还是Object,不能使用具体对象的特有内容,想要是有特有内容,必须向下转型。

列表迭代器(ListIterator)
1.由来:由于Iterator迭代器对数据的操作功能太少,只有对数据进行获取和删除等功能,所以就有了ListIterator迭代器。ListIterator迭代器不仅可以对迭代器进行获取和删除,还可以对迭代器进行添加和修改。
2.特有方法:
  (1)添加
add(E e):将指定的元素插入列表(可选操作)。
  (2)修改 
set(E e):用指定元素替换 next 或 previous 返回的最后一个元素(可选操作)。
  (3)逆向
hasPrevious():逆向遍历列表
previous():返回列表中的前一个元素。

遍历自定义对象及ListIterator特有方法实例(ListIterator)
import java.util.ArrayList;
import java.util.ListIterator;
/*
 * 遍历自定义对象,及ListIterator特有方法
 */
//学生类
class Student{
	private String name;
	private int age;
	Student(String name, int age){
		this.name = name;
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
}

public class ListIteratorDemo {
	public static void main(String[] args) {
		ArrayList al = new ArrayList();
		al.add(new Student("张三", 20));
		al.add(new Student("李四", 21));
		al.add(new Student("王五", 22));
		
		ListIterator it = al.listIterator();
		
		//遍历元素
		sop(al);
		
		//正向遍历列表
		while(it.hasNext()){
			Student stu = (Student)it.next();
			
			if(stu.getName() == "李四"){
				//删除元素
				it.remove();
				//添加元素
				it.add(new Student("赵六", 24));
			}
			else if (stu.getName() == "王五") {
				//修改元素
				it.set(new Student("贺七", 23));
			}		
		}
		
		System.out.println("修改后:");
		//遍历元素
		sop(al);
		
		System.out.println("逆向遍历列表:");
		//逆向遍历列表
		while(it.hasPrevious()){
			Student stu = (Student)it.previous();
			System.out.println("name:" + stu.getName() + ",age:" + stu.getAge());
		}	
	}

	//遍历元素,ArrayList的get方法
	public static void sop(ArrayList al)
	{
		for(int x = 0; x<al.size(); x++){
			Student stu = (Student)al.get(x);
			System.out.println("name:" + stu.getName() + ",age:" + stu.getAge());
		}
	}
}

面试题:并发修改异常
并发修改异常的产生原因:用迭代器遍历集合,用集合去操作集合。
解决方案:
1.使用集合操作。
2.使用列表迭代器操作。


Vector-枚举(Enumeration)
1.特点:
特有的取出方式:枚举(Enumeration)
2.为什么枚举被迭代器替代了?
  因为枚举的名称以及方法的名称都过长。

Vector实例:
import java.util.*;
class VectorDemo 
{
	public static void main(String[] args) {
		Vector v = new Vector();

		v.add("java01");
		v.add("java02");
		v.add("java03");
		v.add("java04");

		Enumeration en = v.elements();

		while(en.hasMoreElements()){
			System.out.println(en.nextElement());
		}
	}
}


LinkedList
1.LinkedList的特有方法:
addFirst();
addLast();
getFirst();
getLast();
获取元素,但不删除元素。如果集合中没有元素,会出现NoSuchElementException

removeFirst();
removeLast();
获取元素,但是元素被删除。如果集合中没有元素,会出现NoSuchElementException

在JDK1.6出现了替代方法。
offerFirst();
offerLast();
peekFirst();
peekLast();
获取元素,但不删除元素。如果集合中没有元素,会返回null。

pollFirst();
pollLast();
获取元素,但是元素被删除。如果集合中没有元素,会返回null。

LinkedList实例:
import java.util.*;

class LinkedListDemo {
	public static void main(String[] args) {
		LinkedList link = new LinkedList();

		link.addLast("java01");
		link.addLast("java02");
		link.addLast("java03");
		link.addLast("java04");

		//sop(link);
		//sop(link.getFirst());
		//sop(link.getFirst());
		//sop(link.getLast());
		//sop(link.removeFirst());
		//sop(link.removeFirst());

		//sop("size="+link.size());
		
		while(!link.isEmpty()){
			//移除并返回此列表的最后一个元素
			sop(link.removeLast());
		}
	}

	public static void sop(Object obj){
		System.out.println(obj);
	}
}

常见的数据结构
加钱:数据结构+算法+UML+设计模式。

数据结构:
栈,队列,数组,链表。
栈:先进后出。    如同一个杯子
队列:先进先出。如同一个水管
数组:查询快,增删慢。
链表:查询慢,增删快。


栈和队列实例(LinkedList模拟)
/*
使用LinkedList模拟一个堆栈或者队列数据结构。
*/

import java.util.*;
class DuiLie
{
	private LinkedList link;

	DuiLie(){
		link = new LinkedList();
	}
	
	public void myAdd(Object obj){
		link.addFirst(obj);
	}
	public Object myGet(){
		return link.removeFirst();
	}
	public boolean isNull(){
		return link.isEmpty();
	}
}

class  LinkedListTest{
	public static void main(String[] args) {
		DuiLie dl = new DuiLie();
		dl.myAdd("java01");
		dl.myAdd("java02");
		dl.myAdd("java03");
		dl.myAdd("java04");

		while(!dl.isNull()){
			System.out.println(dl.myGet());
		}
	}
}


Set接口中常用的类
元素是无序的(存入和取出顺序不一定一致),元素不可以重复。
1.HashSet:
  (1)定义:底层数据结构是哈希表。线程是不同步的,会出现安全问题,存取速度快。
  (2)HashSet的子类LinkedHashSet:有序。
  内部使用散列以加快查询速度,同时使用链表维护元素插入的次序,在使用迭代器遍历Set时,结果会按元素插入的次序显示。 
2.TreeSet:底层的数据结构是二叉树,线程是不同步的,可以对Set集合中的元素进行排序。
  (1)对Set集合中的元素的进行指定(我们指定的比较器)顺序的排序。不同步。TreeSet底层的数据结构就是二叉树。
  
注意:
1.对于判断元素是否存在,以及删除等操作,依赖的方法是元素的hashcode和equals方法。
2.集合只能保存引用数据类型,也就是存储的是对象的地址值,而不是对象本身。集合中的元素相当于引用类型变量。
3.如果使用的集合涉及到了频繁的插入,建议使用LinkedList。


HashSet
import java.util.HashSet;
import java.util.Iterator;

public class HashSetDemo {
	public static void main(String[] args) {
		HashSet hs = new HashSet();
		hs.add("java-01");
		hs.add("java-02");
		hs.add("java-03");
		
		Iterator it = hs.iterator();
		while(it.hasNext()){
			sop(it.next());
		}
	}
	
	public static void sop(Object obj){
		System.out.println(obj);
	}
}
思考:
1.HashSet是如何保证元素唯一性的呢?
  是通过两个方法,hashCode和equals来完成。如果元素hashCode值相同,才会判断equals是否为true。如果元素的hashCode不同,不会调用equals方法。
2.怎么重写hashCode()和equals()方法呢?
  (1)hashCode():把对象的所有成员变量值相加即可。如果是基本数据类型,就加值。如果是引用类型,就加哈希值。
return name.hasCode()+ age * 27;
  (2)equals(obj):先判断是否是同一类型的对象,再把传递进来的对象进行向下转型。再判断对象的内容是否相同。
if(!(obj instanceof Student)) ;
Student stu = (Student)obj; 
return this.name.equals(stu.name) && this.age == stu.age;
   所有成员变量的值比较。基本类型用==,引用类型用equals()。
注意:
1.对于判断元素是否存在,以及删除等操作,依赖的方法是元素的hashCode和equals方法。
2.HashSet在存对象时,自动调用了HashCode()生成哈希码值,如果生成的哈希码值和该HashSet里存在元素的哈希码值相同,再自动调用equals(),反之则不调用equals()。


保证元素唯一性实例:
import java.util.HashSet;
import java.util.Iterator;

/*往hashSet集合中存入自定义对象,姓名和年龄相同为同一个人,重复元素。
*/

//人类
class Person{
	private String name;
	private int age;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	
	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}
	
	public int hashCode(){
		HashSetTest.sop(name + ":hashCode");
		return name.hashCode()+age*50;
	}
	
	public boolean equals(Object obj){
		//判断是不是同一个类型的对象
		if(!(obj instanceof Person)){
			return false;
		}
		Person p = (Person)obj;
		HashSetTest.sop(name + " equals " + p.name);
		return this.name.equals(p.name) && this.age == p.age;
	}
}

public class HashSetTest {
	public static void main(String[] args) {
		HashSet hs = new HashSet();
		hs.add(new Person("张三", 20));
		hs.add(new Person("李四", 20));
		hs.add(new Person("张三", 20));
		hs.add("张三");
		
		Iterator it = hs.iterator();
		while (it.hasNext()) {
			Object obj = it.next();
			if(obj instanceof Person){
				Person p = (Person)obj;
				sop("name:" + p.getName() + ",age:" + p.getAge());
			}else {
				sop(obj);
			}
		}
		
	}
	
	public static void sop(Object obj){
		System.out.println(obj);
	}
}


TreeSet
思考:
TreeSet的排序是如何进行的呢?
通过compareTo或者compare方法中的来保证元素的唯一性。元素是以二叉树的形式存放的。

TreeSet集合两种实现排序方式:
1.自然排序(元素具备比较性)
  让元素自身具备比较性。元素需要实现Comparable接口,覆盖compareTo方法。也种方式也成为元素的自然顺序,或者叫做默认顺序。调用TreeSet的无参构造,要求对象所属的类实现Comparable接口。
2.比较器排序(集合具备比较性)
  当元素自身不具备比较性时,或者具备的比较性不是所需要的。这时就需要让集合自身具备比较性。在集合初始化时,就有了比较方式。调用TreeSet的带参构造,要求构造方法接收一个实现了Comparator接口的对象。

自然排序实例:
import java.util.Iterator;
import java.util.TreeSet;
/**
 * 需求:
 * 1.演示TreeSet的一个特点:把Set集合中元素进行排序
 * 2.往TreeSet集合中存储自定义对象学生。
 *   想按照学生的年龄进行排序。
 * 
 *   记住,排序时,当主要条件相同时,一定判断一下次要条件。
 */

//学生类
class Student implements Comparable{
	private String name;
	private int age;
	Student(String name, int age){
		this.name = name;
		this.age = age;
	}
	
	public String getName(){
		return name;
	}
	
	public int getAge(){
		return age;
	}
	
	//根据年龄来排序
	public int compareTo(Object obj){
		if(!(obj instanceof Student)){
			throw new RuntimeException("不是学生对象");
		}
		//向下转型
		Student stu = (Student)obj;
		System.out.println(this.name + "compareTo age" + stu.name);
		//比较
	    if(this.age > stu.age){
	    	return 1;
	    }else if(this.age == stu.age){
	    	return this.name.compareTo(stu.name);
	    }
		
		return -1;
	}
}


public class TreeSetDemo {
	public static void main(String[] args) {
		/*1.演示TreeSet的一个特点:把Set集合中元素进行排序
		TreeSet ts = new TreeSet();
		ts.add("abc");
		ts.add("abc1");
		ts.add("1abc");
		ts.add("bbc");
		
		//迭代器取出元素
		for(Iterator iterator = ts.iterator(); iterator.hasNext();){
			iptOut(iterator.next());
		}
		*/
		
		//2.往TreeSet集合中存储自定义对象学生。
		TreeSet ts = new TreeSet();
		ts.add(new Student("张三", 18));
		ts.add(new Student("李四", 20));
		ts.add(new Student("王五", 29));
		ts.add(new Student("赵六", 18));
		
		//迭代器取出元素
		for(Iterator iterator = ts.iterator(); iterator.hasNext();){
			Student stu = (Student)iterator.next();
			out(stu.getName() + ":" + stu.getAge());
		}
	}
	
	public static void out(Object e){
		System.out.println(e);
	}
}

比较器排序实例:
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;

/*
当元素自身不具备比较性,或者具备的比较性不是所需要的。
这时需要让容器自身具备比较性。
定义了比较器,将比较器对象作为参数传递给TreeSet集合的构造函数。

定义一个类,实现Comparator接口,覆盖compare方法。
*/
//学生类,实现Comparable借口,使元素具备比较性
class Student implements Comparable{
	private String name;
	private int age;
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	Student(String name, int age){
		this.name = name;
		this.age = age;
	}
	
	//根据学生的年龄来比较,当年龄相同时再比较姓名
	public int compareTo(Object obj){
		//判断是不是本类对象
		if(!(obj instanceof Student)){
			throw new RuntimeException("不是Student类对象");
		}
		//向下转型
		Student stu = (Student)obj;
		System.out.println(this.name + "compareTo age" + stu.name);
		//比较
		if(this.age > stu.age){
			return 1;
		}else if (this.age == stu.age) {
			System.out.println(this.name + "compareTo name" + stu.name);
			return this.name.compareTo(stu.name);
		}
		return -1;
	}
}

//比较器
class MyComparator implements Comparator{
	public int compare(Object obj1, Object obj2){
		Student stu1 = (Student)obj1;
		Student stu2 = (Student)obj2;
		//先比较姓名,再比较年龄
		int num = stu1.getName().compareTo(stu2.getName());
		
		if(num == 0){
			if(stu1.getAge() > stu2.getAge()){
				return 1;
			}else if(stu1.getAge() < stu2.getAge()){
				return -1;
			}
			return 0;
		}
		
		return num;
	}
}

class TreeSetDemo2 {
	public static void main(String[] args) {
		TreeSet ts = new TreeSet(new MyComparator());
		ts.add(new Student("张三 ",20));
		ts.add(new Student("李四 ",19));
		ts.add(new Student("王五 ",22));
		ts.add(new Student("张三 ",20));
		
		Iterator it = ts.iterator();
		while (it.hasNext()) {
			Student stu = (Student)it.next();
			System.out.println("name:" +stu.getName() + ",age:" + stu.getAge());
		}
	}
}
注意:当两种排序都存在时,以比较器为主。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值