黑马程序员——集合框架

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

集合框架


一、概述

集合框架是为表示和操作集合而规定的一种统一的标准的体系结构。任何集合框架都包含三大块内容:对外的接口、接口的实现和对集合运算的算法。

  • 接口:是代表集合的抽象数据类型。接口允许集合独立操纵其代表的细节。在面向对象的语言,接口通常形成一个层次。
  • 实现(类):是集合接口的具体实现。从本质上讲,它们是可重复使用的数据结构。
  • 算法:是实现集合接口的对象里的方法执行的一些有用的计算,例如:搜索和排序。这些算法被称为多态,那是因为相同的方法可以在相似的接口上有着不同的实现。

Java集合框架:

1. 什么是框架:类库的集合

2. 集合框架:用来表示和操作的统一的框架,包含了实现集合的接口与类

3. 集合:存放数据的容器。集合框架包含了两部分:一部分是接口,一部分是类

4. 为什么会出现接口:因为集合框架中的很多类,功能是相似的,所以用接口来规范类。


主要结构图:



注:1. 在集合框架中,接口Map和Collection在层次结构上没有任何亲缘关系,它们是截然不同的。

        2. 不要简单的认为集合类就这些,jdk中集合类有很多这些是我们经常用到的而已,Collection、List、Set、Queue和Map都是接口(Interface),不是具体的类实现。


为什么出现集合类?

面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储,集合就是存储对象最常用的一种方式。

 

数组和集合类同是容器,有何不同?

数组虽然也可以存储对象,但长度是固定的;集合长度是可变的。

数组中可以存储基本数据类型,集合只能存储对象。

 

集合类的特点:

集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象。

 

为什么会出现这么多的容器呢?

因为每一个容器对数据的存储方式都有不同,这个存储方式称之为:数据结构。



二、共性方法

1,add方法的参数类型是Object。以便于接收任意类型对象。

2,集合中存储的都是对象的引用(地址)。



三、迭代器

什么是迭代器呢?

其实就是集合的取出元素的方式。

  就把取出方式定义在集合的内部。这样取出方式就可以直接访问集合内部的元素。那么取出方式就被定义成了内部类。

而每一个容器的数据结构不同,所以取出的动作细节也不一样。但是都有共性内容:判断和取出。那么可以将这些共性抽取。

那么这些内部类都符合一个规则,该规则是Iterator。


如何获取集合的取出对象呢?

通过一个对外提供的方法。iterator();

格式:

                ArrayList al = new ArrayList();

		//1.添加元素。
		al.add("java01");
		al.add("java02");
		
                Iterator it = al.iterator(); //获取迭代器,用于取出集合中的元素。
		while (it.hasNext())
		{
			System.out.println(it.next());
		}


例如:

import java.util.*;

class CollectionDemo 
{
	public static void main(String[] args) 
	{
		//method_2();
		method_get();
	}

	public static void method_get()
	{
		ArrayList al = new ArrayList();

		//1.添加元素。
		al.add("java01");
		al.add("java02");
		al.add("java03");
		al.add("java04");

		/*
		Iterator it = al.iterator(); //获取迭代器,用于取出集合中的元素。
		while (it.hasNext())
		{
			sop(it.next());
		}
		*/
		
		for (Iterator it=al.iterator(); it.hasNext(); )
		{
			sop(it.next());
		}
	}

	public static void method_2()
	{
		ArrayList al1 = new ArrayList();
		al1.add("java01");
		al1.add("java02");
		al1.add("java03");
		al1.add("java04");

		ArrayList al2 = new ArrayList();
		al2.add("java03");
		al2.add("java04");
		al2.add("java05");
		al2.add("java06");

		//al1.retainAll(al2);//取交集。al1中只会保留和al2中相同的元素。
		al1.removeAll(al2);

		sop("al1:"+al1);
		sop("al2:"+al2);
	}

	public static void base_method()
	{
		//创建一个集合容器,使用collection接口的子类。ArrayList
		ArrayList al = new ArrayList();

		//1.添加元素。
		al.add("java01");  //add(Object obj);
		al.add("java02");
		al.add("java03");
		al.add("java04");

		//打印原集合
		sop("原集合:"+al); //结果:[java01, java02, java03, java04]

		//3.删除元素。
		//al.remove("java02"); //结果:[java01, java03, java04]
		//al.clear();  //清空集合。结果为:[]

		//4.判断元素。
		sop("java03是否存在:"+al.contains("java03"));
		sop("集合是否为空?"+al.isEmpty());

		//2.获取个数,集合长度。
		sop("size:"+al.size());
		
		//打印改变后的集合。
		sop(al);
	}

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



四、List集合共性方法

1、ListIterator

List集合特有的迭代器:ListIterator是Iterator的子接口。在迭代时,不可以通过集合对象的方法操作集合中的元素。

因为会发生ConcurrentModificationException异常。所以,在迭代时,只能用迭代器的方法操作元素。可是Iterator方法是有限的,只能对元素进行判断,取出,删除的操作。如果想要其他的操作,如添加、修改等。就需要使用其子接口,ListIterator。

该接口只能通过List集合的ListIterator方法获取。



2、Collection

|--List:元素是有序的;元素可以重复。因为该集合体系有索引。

|--ArrayList:底层的数据结构使用的是数组结构。特点:查询速度很快,但是增删稍慢。线程不同步。

|--LinkedList:底层使用的链表数据结构。特点:增删速度很快,查询稍慢。

|--Vector:底层是数组数据结构。线程同步。被ArrayList替代了。

|--Set:元素是无序的;元素不可以重复。

 

List:

特有方法,凡是可以操作角标的方法都是该体系特有的方法。

 

add(index,element);

addAll(index,Collection);

remove(index);

set(index,element);

get(index);

subList(from,to);

listIterator();

int indexOf(obj):获取指定元素的位置。

ListIterator listIterator();

 例如:

import java.util.*;

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

	public static void method()
	{
		ArrayList al = new ArrayList();

		//添加元素。
		al.add("java01");
		al.add("java02");
		al.add("java03");
		
		sop("原集合是:"+al);
		//在指定位置添加元素。
		al.add(1,"java09");
		
		//删除指定位置的元素
		//al.remove(2);

		//修改元素。
		//al.set(2,"java07");

		//通过角标获取元素。
		sop("get(1):"+al.get(1));

		sop("改变后是:"+al);
		
		
		//获取所有元素。
		for (int x=0; x<al.size(); x++)
		{
			System.out.println("al("+x+")="+al.get(x));
		}
		
		//用迭代器获取所有元素。
		Iterator it=al.iterator();
		while (it.hasNext())
		{
			sop("next:"+it.next());
		}

		//通过indexOf获取对象的位置。
		sop("index="+al.indexOf("java02"));

		List sub=al.subList(1,3);
		sop("sub="+sub);
	}

	public static void main(String[] args) 
	{
		//演示列表迭代器。
		ArrayList al = new ArrayList();

		//添加元素。
		al.add("java01");
		al.add("java02");
		al.add("java03");

		sop("原集合是:"+al);

		ListIterator li=al.listIterator();

		//sop("hasPrevious():"+li.hasPrevious());

		while (li.hasNext())
		{
			Object obj=li.next();
			if(obj.equals("java02"))
				li.set("java06");
				//li.add("java009");
		}

		while (li.hasPrevious())
		{
			sop("previous::"+li.previous());
		}

		//sop("hasNext():"+li.hasNext());
		//sop("hasPrevious():"+li.hasPrevious());

		sop("改变后的集合:"+al);

		
		/*
		//在迭代过程中,准备添加或者删除元素。
		Iterator it=al.iterator();
		while (it.hasNext())
		{
			Object obj=it.next();

			if(obj.equals("java02"))
				it.remove();  //将java02的引用从集合中删除了。
				//al.add("java008");

			sop("obj="+obj);
		}
		sop("改变后的集合:"+al);
		*/


	}
}

3、Vector中的枚举

枚举就是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());
		}
	}
}


4、LinkedList

LinkedList:特有方法:

addFirst();

addLast();

getFirst();

getLast();

获取元素,但不删除元素。如果集合中没有元素,会出现NoSuchElementException。

 

removeFirst();

removeLast();

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

 

在JDK1.6出现了替代方法。

offerFirst();

offerLast();

peekFirst();

peekLast();

获取元素,但不删除元素。如果集合中没有元素,会返回null。

 

pollFirst();

pollLast();

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

例如:

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");

		while (!link.isEmpty())
		{
			System.out.println(link.removeFirst());
		}
	}

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


5、ArrayList

/*
例如:去除ArrayList集合中的重复元素。
*/

import java.util.*;

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

	public static void main(String[] args) 
	{
		ArrayList al=new ArrayList();
		al.add("java01");
		al.add("java02");
		al.add("java01");
		al.add("java02");
		al.add("java01");
		al.add("java03");

		/*
		在迭代时循环中next调用一次,就要hasNext判断一次。
		
		Iterator it=al.iterator();
		while (it.hasNext())
		{
			sop(it.next()+"......"+it.next());
		}
		*/
		
		sop(al);
		al=singleElement(al);
		sop(al);
	}

	public static ArrayList singleElement(ArrayList al)
	{
		//定义一个临时容器。
		ArrayList newAl=new ArrayList();

		Iterator it=al.iterator();
		while (it.hasNext())
		{
			Object obj=it.next();
			if(!newAl.contains(obj))
				newAl.add(obj);
		}
		return newAl;
	}
}



五、Set

Set集合的功能和Collection是一致的。

|--Set:元素是无序(存入和取出的顺序不一定一致);元素不可以重复。

|--HashSet:底层数据结构是哈希表。线程是非同步的。

HashSet是如何保证元素唯一性的呢?

是通过元素的两个方法,hashCode和equals来完成。

如果元素的HashCode值相同,才会判断equals是否为true。如果元素的hashcode值不同,不会调用equals。

注意:对于判断元素是否存在,以及删除等操作,依赖的方法时元素的hashCode和equals方法。

|--TreeSet:可以对Set集合中的元素进行排序。

底层数据结构是二叉树。

保证元素唯一性的依据:compareTo方法return 0;

 

TreeSet排序的第一种方式:让元素自身具备比较性。

元素需要实现Comparable接口,覆盖compareTo方法。这种方式也称为元素的自然顺序,或者叫做默认顺序。

 

TreeSet的第二种排序方式:

当元素自身不具备比较性时,或者具备的比较性不是所需要的。这时就需要让集合自身具备比较性。在集合初始化时,就有了比较方式。

 

记住:排序时,当主要条件相同时,一定要判断一下次要条件。


1、HashSet

例如:

import java.util.*;

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

	public static void main(String[] args) 
	{
		HashSet hs=new HashSet();

		hs.add(new Person("a1",11));
		hs.add(new Person("a2",12));
		hs.add(new Person("a3",13));
		hs.add(new Person("a2",12));
		
		//sop("a1:"+hs.contains(new Person("a2",12)));
		hs.remove(new Person("a3",13));

		Iterator it=hs.iterator();
		while (it.hasNext())
		{
			Person p=(Person)it.next();
			sop(p.getName()+"::"+p.getAge());
		}
	}
}

class Person
{
	private String name;
	private int age;
	Person(String name,int age)
	{
		this.name=name;
		this.age=age;
	}

	public int hashCode()
	{
		System.out.println(this.name+"...hashCode");
		return name.hashCode()+age*39;
		//return 60;
	}

	public boolean equals(Object obj)
	{
		if(!(obj instanceof Person))
			return false;

		Person p=(Person)obj;
		System.out.println(this.name+"...equals..."+p.name);
		return this.name.equals(p.name) && this.age==p.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;
	}
}


2、TreeSet

                TreeSet ts=new TreeSet();

		ts.add("cba");
		ts.add("abcd");
		ts.add("aaa");
		ts.add("bca");
		
		Iterator it=ts.iterator();
		while (it.hasNext())
		{
			System.out.println(it.next());
		}


3、实现Comparator方式排序

当元素自身不具备比较性,或者具备的比较性不是所需要的。这时需要让容器自身具备比较性。

那么定义一个比较器,将比较器对象作为参数传递给TreeSet集合的构造函数。

当两种排序都存在时,以比较器为主。

怎样定义一个比较器呢?

定义一个类,实现Comparator接口,覆盖compare方法。

例如:

import java.util.*;
class Student implements Comparable //该接口强制让学生具备比较性。
{
	private String name;
	private int age;

	Student(String name,int age)
	{
		this.name=name;
		this.age=age;
	}
	//覆盖compareTo方法,按年龄排序
	public int compareTo(Object obj)
	{
		if(!(obj instanceof Student))
			throw new RuntimeException("不是学生对象!");
		Student s=(Student)obj;
		
		//System.out.println(this.name+"...compareto..."+s.name);
		if(this.age>s.age)
			return 1;
		if(this.age==s.age)
		{
			return this.name.compareTo(s.name);
		}
		return -1;
	}

	public String getName()
	{
		return name;
	}
	public int getAge()
	{
		return age;
	}
}

class TreeSetDemo2 
{
	public static void main(String[] args) 
	{
                //TreeSet ts=new TreeSet();
		TreeSet ts=new TreeSet(new MyCompare());

		ts.add(new Student("lisi02",22));
		ts.add(new Student("lisi02",21));
		ts.add(new Student("lisi007",20));
		ts.add(new Student("lisi09",19));
		ts.add(new Student("lisi06",18));
		ts.add(new Student("lisi007",29));
		
		Iterator it=ts.iterator();
		while (it.hasNext())
		{
			Student stu=(Student)it.next();
			System.out.println(stu.getName()+"......"+stu.getAge());
		}
	}
}

//实现Comparator接口,覆盖compare方法。按姓名排序。
class MyCompare implements Comparator
{
	public int compare(Object o1,Object o2)
	{
		Student s1=(Student)o1;
		Student s2=(Student)o2;

		//return s1.getName().compareTo(s2.getName());

		int num=s1.getName().compareTo(s2.getName());
		if (num==0)
		{
			return new Integer(s1.getAge()).compareTo(new Integer(s2.getAge()));
		}
		return num;
	}
}



六、泛型

泛型:JDK1.5版本以后出现的新特性。用于解决安全问题,是一个类型安全机制。

 

好处:

1,将运行时期出现的问题ClassCastException,转移到了编译时期。方便于程序员解决问题。让运行时期问题减少,安全。

2,避免了强制转换的麻烦。

 

泛型格式:通过<>来定义要操作的引用数据类型。

在使用java提供的对象时,什么时候写泛型呢?

  通常在集合框架中很常见,只要见到<>就要定义泛型。其实<>就是用来接收类型的。

当使用集合时,将集合中要存储的数据类型作为参数传递到<>中即可。

例如:

import java.util.*;

class GenericDemo 
{
	public static void main(String[] args) 
	{
		ArrayList<String> al=new ArrayList<String>(); //泛型

		al.add("abc01");
		al.add("abc0991");
		al.add("abc014");

		Iterator<String> it=al.iterator();
		while (it.hasNext())
		{
			String s=it.next();
			System.out.println(s+":"+s.length());
		}
	}
}


1、泛型类

什么时候定义泛型类?

当类中要操作的引用数据类型不确定的时候。早期定义Object来完成扩展。现在定义泛型来完成扩展。

例如:

class Worker
{
}
class Student
{
}

//泛型前做法
class Tool
{
	private Object obj;
	public void setObject(Object obj)
	{
		this.obj=obj;
	}
	public Object getObject()
	{
		return obj;
	}
}

//泛型类
class Utils<QQ>
{
	private QQ q;
	public void setObject(QQ q)
	{
		this.q=q;
	}
	public QQ getObject()
	{
		return q;
	}
}

class GenericDemo3 
{
	public static void main(String[] args) 
	{
		Utils<Worker> u=new Utils<Worker>();
		u.setObject(new Worker());
		//Worker w=(Worker)u.getObject();
		Worker w=u.getObject(); //不用强转
	}
}


2、泛型方法

泛型类定义的泛型,在整个类中有效。如果被方法使用,那么泛型类的对象明确要操作的具体类型后,所有要操作的类型就已经固定了。

  为了让不同方法可以操作不同类型,而且类型还不确定。那么可以将泛型定义在方法上。


3、静态方法泛型

特殊之处:

静态方法不可以访问类上定义的泛型。如果静态方法操作的引用数据类型不确定,可以将泛型定义在方法上。


泛型定义在方法上,放在哪?

<>放在返回值类型前面,修饰符的后面。

例如:

class Demo<T>	//泛型类
{
	public void show(T t)
	{
		System.out.println("show:"+t);
	}
	public <Q> void print(Q q)	//泛型方法
	{
		System.out.println("print:"+q);
	}
	public static <W> void method(W w)	//静态方法泛型
	{
		System.out.println("method:"+w);
	}
}

class GenericDemo4 
{
	public static void main(String[] args) 
	{
		Demo<String> d=new Demo<String>();
		d.show("haha");
		//d.show(4); //错误;泛型的类型已经固定。
		d.print(5);
		d.print("hehe");

		Demo.method("hahahaha"); //静态方法,类名调用
	}
}


4、泛型接口

//泛型定义在接口上。
interface Inter<T>
{
	void show(T t);
}

/*
//第一种方式:
class InterImpl implements Inter<String>
{
	public void show(String t)
	{
		System.out.println("show:"+t);
	}
}
*/

//第二种方式:
class InterImpl<T> implements Inter<T>
{
	public void show(T t)
	{
		System.out.println("show:"+t);
	}
}

class GenericDemo5 
{
	public static void main(String[] args) 
	{
		/*
		InterImpl i=new InterImpl();
		i.show("haha");
		*/
		
		InterImpl<Integer> i=new InterImpl<Integer>();
		i.show(4);
	}
}



七、泛型限定

? 通配符。也可以理解为占位符。

泛型的限定:

? extends E:可以接收E类型或者E的子类型。上限。

? super E:可以接收E类型或者E的父类型。下限。

例如:


import java.util.*;

class GenericDemo6 
{
	public static void main(String[] args) 
	{
		ArrayList<Person> al=new ArrayList<Person>();
		al.add(new Person("abc1"));
		al.add(new Person("abc2"));
		al.add(new Person("abc3"));

		ArrayList<Student> al1=new ArrayList<Student>();
		al1.add(new Student("abc--1"));
		al1.add(new Student("abc--2"));
		al1.add(new Student("abc--3"));

		printColl(al1); //ArrayList<Person> al=new ArrayList<Student>(); Error
	}

	public static void printColl(ArrayList<? extends Person> al) //泛型限定
	{
		Iterator<? extends Person> it=al.iterator();
		while (it.hasNext())
		{
			System.out.println(it.next().getName());
		}
	}
}

class Person
{
	private String name;
	Person(String name)
	{
		this.name=name;
	}
	public String getName()
	{
		return name;
	}
}

class Student extends Person
{
	Student(String name)
	{
		super(name);
	}
}




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


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值