黑马程序员--java基础复习之集合

------ Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------


集合


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

即集合就是用来存储对象的。
数据多了用对象存储,对象多了就用集合存储。

2、数组也是容器,和集合有什么不同?
数组中虽然也可以存储对象,但长度是固定的;集合长度是可变的。
数组中可以存储基本数据类型,集合只能存储对象。

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


Collection
Collection 层次结构 中的根接口

集合框架的构成





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

Collection接口
Collection是集合框架中的常用接口。其下有两个子接口:List(列表),Set(集)。
Collection
|--List:元素是有序的,元素可以重复。因为该集合体系有索引。
|--Set:元素是无序的,元素不可以重复。

Collection接口中的常用方法
1、添加元素
        add(Object obj); //add方法的参数类型是Object。以便于接收任意类型对象。
2、删除元素
        remove(Object obj);
        removeAll(另一集合);//调用者只保留另一集合中没有的元素。
        clear();//清空集合
3、判断元素
        contains(Object obj);//判断是否存在obj这个元素
        isEmpty();//是否元素列表为空
4、获取集合长度
        size();
5、取交集
        retainAll(另一集合);//调用者只保留两集合的共性元素。
注:集合中存储的都是对象的引用(地址)

什么是迭代器呢?其实就是集合的取出元素的方式。

对于集合的元素取出这个动作:
        当不足以用一个函数来描述,需要用多个功能来体现,所以就将取出这个动作封装成一个对象来描述。就把取出方式定义在集合的内部,这样取出方式就可以直接访问集合内部的元素。那么取出方式就被定义成了内部类。 而每一个容器的数据结构不同,所以取出的动作细节也不一样。但是都具有共性内容: 判断和取出。那么就可以将这些共性抽取。那么这些内部类都符合一个规则(或者说都抽取出来一个规则)。该规则就是Iterator。通过一个对外提供的方法:iterator();,来获取集合的取出对象。
 因为Collection中有iterator方法,所以每一个子类集合对象都具备迭代器。


Iterator接口中的方法

1、判断:boolean  hasNext()    如果仍有元素可以迭代,则返回 true
2、获取:next()          返回迭代的下一个元素
3、移除:void remove()  从迭代器指向的 collection 中移除迭代器返回的最后一个元素


取出集合中的元素:
<span style="font-size:14px;"><span style="white-space:pre">		</span>//获取所有元素(通过循环)
		for(int x=0;x<al.size();x++)
		{
			sop("al("+x+")"+al.get(x));
		}
		
		//获取所有元素(通过迭代器)
		Iterator it=al.iterator();
		while(it.hasNext())
		{
			sop(it.next());
		}</span>


List

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

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

一、增:
add(index,element)
addAll(index,Collection)
二、删:
remove(index)
三、修改:
set(index,element)
四、查
get(index)
subList(from,to)
listIterator()
previous();获取前一位元素


判断:
boolean hasNext()  以正向遍历列表时,如果列表迭代器有多个元素,则返回 true 
hasPrevious()  如果以逆向遍历列表,列表迭代器有多个元素,则返回 true。

ListIterator是List集合特有的迭代器。ListIterator是Iterator的子接口。
在迭代时,不可以通过集合对象的方法操作集合中的元素,因为会发生
ConcurrentModificationException(并发修改异常)异常

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


该接口只能通过List集合的listiterator() 方法来获取

下面通过代码来看一下List中方法的应用
import java.util.*;

class ListDemo
{
	public  static void main(String[] args)
	{
		Methods();
		theListIterator();
	}
	
	//演示列表迭代器
	public static void theListIterator()
	{
	  	ArrayList al=new ArrayList();		
		al.add("java01");
		al.add("java02");
		al.add("java03");
		al.add("java04");
		al.add("java05");		
		sop("原集合:"+al);
		
		/**************ListIterator(增删改查)***************/
		ListIterator li=al.listIterator();
		
		//从前往后取,判断是否有下一个元素
		while(li.hasNext())
		{
			Object obj=li.next();
			if(obj.equals("java01"))
				li.add("java011");//通过ListIterator添加元素
			if(obj.equals("java02"))
				li.remove();  //删除元素
			if(obj.equals("java03"))
				li.set("java033");//用指定元素替换 next 或 previous 返回的最后一个元素
			
			sop(obj);
		}
		//取完后判断是否有下一位,是否有上一位元素
		sop("是否有下一位(hasNext):"+li.hasNext());
		sop("是否有前一位(hasPrevious):"+li.hasPrevious());
		sop(al);
		sop("-----------从后往前-----------");
		//判断是否有前一位
		while(li.hasPrevious())
		{
			sop(li.previous());
		}
						
	}
	
	//List中的基本方法
	public static void Methods()
	{
		ArrayList al=new ArrayList();		
		al.add("java01");
		al.add("java02");
		al.add("java03");
		al.add("java04");
		al.add("java05");		
		sop("原集合:"+al);
		//添加
		al.add(3,"java007");
		sop("add():"+al);
		//删除
		al.remove(2);
		sop("remove(2):"+al);
		//修改
		al.set(1,"java00");
		sop("set:"+al);
		//查
		sop("get(3):"+al.get(3));
		List al1=al.subList(1,3);
		sop(al1);
		
		//获取所有元素(通过循环)
		for(int x=0;x<al.size();x++)
		{
			sop("al("+x+")"+al.get(x));
		}
		
		//获取所有元素(通过迭代器)
		Iterator it=al.iterator();
		while(it.hasNext())
		{
			sop(it.next());
		}
		
		//通过indexOf获取对象的位置
		sop(al.indexOf("java007"));
		
	}
	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
}

结果:

上面就是List中的方法的使用,以及Iterator和ListIterator的使用

ListIterator特有的方法
add(obj);//增加
set(obj);//修改为obj
hasPrevious();//判断前面有没有元素
previous();//取前一个元素

下面再看一个代码段:
	ArrayList al=new ArrayList();		
		al.add("java01");
		al.add("java02");
		al.add("java03");
		al.add("java04");
		al.add("java05");		
		
		
		//迭代器
		Iterator it=al.iterator();
		
		while(it.hasNext())
		{
			Object obj=it.next();
			if(obj.equals("java02"))
			//al.remove("java02"); 此处会引发并发操作异常
			it.remove();//从迭代器指向的 collection 中移除迭代器返回的最后一个元素。所以这里没有参数
			sop(obj); //这里仍然能打印出删除的那个元素:因为集合中存放的元素的地址值,将地址值从集合中删除
				//后,obj仍指向对象在内存中的地址
		}
		sop(al);
		

当我们通过迭代器对集合中的元素进行移除操作后,我们实际上移除的是对象在集合中的引用(地址)


下面来看一下List的一个实现类LinkedList

LinkedList:底层数据结构是链表结构。特点:查询很快。但是增删稍慢。线程不同步

特有方法:
addFirst(E e):将指定元素插入此列表的开头。
addLast(E e):将指定元素添加到此列表的结尾
getFirst() :返回此列表的第一个元素
getLast() :返回此列表的最后一个元素
获取元素,但不删除元素

removeFirst()
removeLast()

获取元素并删除。

如果集合中没有元素,会出现NoSuchElementException
在JDK 1.6 出现了替代方法  (若集合中没有元素则返回null)

offerFirst(E e):在此列表的开头插入指定的元素
offerLast(E e):在此列表末尾插入指定的元素


peek() 获取但不移除此列表的头(第一个元素)。
peekFirst();获取但不移除此列表的第一个元素;如果此列表为空,则返回 null。
peekLast();获取但不移除此列表的最后一个元素;如果此列表为空,则返回 null


pollFirst()  获取并移除此列表的第一个元素;如果此列表为空,则返回 null
pollLast(): 获取并移除此列表的最后一个元素;如果此列表为空,则返回 null


通过一段代码来看看这些方法的使用:
import java.util.*;
class LinkedListDemo
{
	public static void main(String[] args)
	{
		theMethods();
	}
	
	public static void theMethods()
	{
		LinkedList link=new LinkedList();
		link.add("java01");
		link.addFirst("java001");
		link.addFirst("java002");
		link.addFirst("java003");
		link.addFirst("java004");
		link.addFirst("java005");
		link.addLast("java02");
		sop(link);
		
		//获取第一个元素
		sop(link.getFirst());
		//获取最后一个元素
		sop(link.getLast());
		//删除第一个元素
		sop(link.removeFirst());
		//删除最后一个元素
		sop(link.removeLast());
		sop(link.removeLast());
		//在此列表的开头插入指定的元素,等同于addFirst()
		link.offerFirst("java00");
		/*************通过列表迭代器的方式***********/
		ListIterator li=link.listIterator();
		while(li.hasNext())
		{
			sop(li.next());
		}
			/*
		sop(link);
		//通过for循环的方式
		for(int x=0;x<link.size();x++)
		{
			sop("link["+x+"]:"+link.get(x));
		}			*/
	}
	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
}

结果:

注意:ArrayList和LinkedList删除remove和包含contains依赖的都是equals方法
(这个方法不是我们调用的,而是集合底层自己调用的)



Vector
Vector:底层是数组数据结构。线程同步。增删查都慢。被ArrayList替代了。

ArrayList  是可变长度数组,初始长度为10,当长度超过10,则新建一个数组,长度增加50%,为15,然后将数组中的元素添加到新数组中
Vector 是可变长度数组,初始长度为10,当长度超过10,则新建一个数组,长度增加100%,为20。



枚举Enumeration
枚举:
        就是Vector特有的取出方式。Vector有三种取出方式。
        其实枚举和迭代是一样的。因为枚举的名称以及方法的名称都过长。所以被迭代器取代了。
特有方法:
         addElement(obj);//添加元素,相当于add(obj);
         Enumerationelements();//Vector特有取出方式(枚举)
         hasMoreElements();//相当于Iterator的hasNext()方法
         nextElements();//相当于Iterator的next()方法

Vector 有三种取出元素的方式 
一、通过遍历,循环,用get()方法依次取出  
二、使用迭代器  Iterator
三、使用枚举 Enumeration

例子:
import java.util.*;
class VectorDemo
{
	public static void main(String[] args)
	{
		vectorEnumeration();
	}
	public static void vectorEnumeration()
	{
		Vector v=new Vector();
		v.add("java01");
		v.add("java02");
		v.add("java03");
		v.add("java04");
		
		//枚举就是Vector特有的取出方式,类似于迭代器
		Enumeration en=v.elements();  //获取vector对象中的所有元素
		
		while(en.hasMoreElements())
		{
			System.out.println(en.nextElement());
		}
	}
}

输出:


枚举就是Vector特有的取出方式。发现枚举和迭代器很像。其实枚举和迭代是一样的。
因为枚举的名称及方法的名称都过长,所以被迭代器取代了。


Set
|--Set:元素是无序(存入和取出的顺序不一定一致)的,元素不可以重复。
|--HashSet:底层数据结果是哈希表。
|--TreeSet

HashSet是如何保证元素唯一性的呢?
是通过元素的两个方法,hashCode和equals来完成的。
如果元素的HashCode值相同,才会判断equals是否为true.
如果元素的hashCode值不同,不会调用equals


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

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


看下一段代码,可以看出Set 集合中的元素是无序的,元素不可以重复
import java.util.*;
class HashSetDemo
{
	public static void main(String[] args)
	{
			method_1();
	}
	
	public static void method_1()
	{
		//建立一个HashSet类对象
	   HashSet hs=new HashSet();
		hs.add("java01");
		hs.add("java02");
		hs.add("java01");
		hs.add("java04");
		
		//通过迭代器取出元素
		Iterator it=hs.iterator();
		while(it.hasNext())
		{
			sop(it.next());
		}
	}
	
	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
}

结果:   可以看出添加元素时,重复的那个元素并未添加进集合中去。

应用实例:
/*
需求:往hashSet集合中存入自定义对象。如人,姓名和年龄相同即为同一个人,即重复元素

注意:对于判断元素是否存在,以及删除等操作,依赖的方法是元素的hashcode和equals方法
这两个方法不是我们自己调用的,是集合底层自己调用的
*/

import java.util.*;
class HashSetTest
{
	public static void main(String[] args)
	{
		//创建一个HashSet集合
		HashSet hs=new HashSet();
		//向集合中添加元素
		hs.add(new Person("张三",20));
		hs.add(new Person("李四",26));
		hs.add(new Person("张三",20));
		hs.add(new Person("王五",21));
		
		//从集合中删除元素
		hs.remove(new Person("张三",20));
		//通过迭代器取出集合中的元素
		Iterator it=hs.iterator();
		while(it.hasNext())
		{
			//向下转型,多态的应用
			Person p=(Person)it.next();
			sop(p.getName()+"....."+p.getAge());
		}
	}
	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
	
	
}

//创建一个Person类
class Person
{
	private String name;
	private int age;
	Person(String name,int age)
	{
		this.name=name;
		this.age=age;
	}
	public String getName()
	{
		return this.name;
	}
	public int getAge()
	{
		
		return this.age;
	}
	//复写父类中的hashCode方法
	public int hashCode()
	{
		System.out.println(name+"....hashCode.....");
		//返回哈希值
		return name.hashCode()+age*39;
	}
	
	//复写父类中的equals方法
	public boolean equals(Object obj)
	{
		if(obj instanceof Person)
		{
			Person p=(Person)obj;
			System.out.println(this.name+"....equals...."+p.name);
			return this.name.equals(p.name)&&this.age==p.age;
		}
		return false;
	}
	
}

注意:对于判断元素是否存在,以及删除等操作,依赖的方法是元素的hashcode和equals方法
这两个方法不是我们自己调用的,是集合底层自己调用的



看一下Set接口下的另一个实现类TreeSet

TreeSet:可以对Set 集合中的元素进行排序。底层数据结构是二叉树。
保证元素唯一性的依据是:compartTo方法 return 0.

TreeSet 排序的第一种方式:
让元素自身具备比较性。元素需要实现Comparable接口,覆盖compareTo,这种排序方式也称为自然排序,或者叫默认顺序

通过例子来说明:
/*需求:往TreeSet集合中存储自定义学生对象。
按照学生的年龄进行排序

记住:排序时,当主要条件相同时,一定要判断一下次要条件
*/
import java.util.*;
class TreeSetDemo
{
	public static void main(String[] args)
	{
		//创建一个TreeSet集合
		TreeSet ts=new TreeSet();
		
		//向集合中添加对象(元素)
		ts.add(new Student("zhangsan",20));		
		ts.add(new Student("lisi",21));		
		ts.add(new Student("wangwu",22));		
		ts.add(new Student("zhaoliu",22));		
				
		//使用迭代器取出元素列表
		Iterator it=ts.iterator();
		while(it.hasNext())
		{
			Student s=(Student)it.next();
			sop(s.getName()+"...."+s.getAge());
		}
		
	}
	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
}

class Student implements Comparable  //该接口强制让学生类具备比较性
{
	private String name;
	private int age;
	public String getName()
	{
		return this.name;
	}
	public int getAge()
	{
		return this.age;
	}
	
	Student(String name,int age)
	{
		this.name=name;
		this.age=age;
	}
	
	//复写Comparable接口中的compareTo方法
	public int compareTo(Object obj)
	{
		/*
		当这个方法只有一句代码:return 1  ,则是按添加顺序排序
		若return -1,则按添加顺序倒序排序
		若返回0,则只有一个元素
		*/
		if(!(obj instanceof Student))
			throw new RuntimeException("不是Student类型");
		Student s=(Student)obj;
		
		int num=new Integer(this.age).compareTo(new Integer(s.age));
		if(num==0) 
			return this.name.compareTo(s.name);
		return num;
		/*用以下方式也可以
		if(this.age>s.age)
			return 1;					
		if(this.age==s.age) //当年龄相等时,判断次要条件		
			return this.name.compareTo(s.name);
		return -1;
		*/
	}
	
}
以上是第一种方式,两个步骤: A、元素实现Comparable接口    B、复写接口中的compareTo() 方法

第二种方式:比较器     ------>当元素自身不具备比较性或者具备的比较性不是所需要的,这时就需要让集合自身具备比较性。
在集合初始化时,就有了比较方法。
步骤:A、定义一个类,实现Comparator接口     B、覆盖compare方法
C、将比较器对象传递给集合构造函数。

还是通过代码来说明,,下面将两种方式在一个例子中实现作对比:
import java.util.*;
class TreeSetDemo1
{
	public static void main(String[] args)
	{
		TreeSet ts=new TreeSet(new MyCompare());
		
		ts.add(new Student("zhangsan",20));		
		ts.add(new Student("lisi",21));		
		ts.add(new Student("wangwu",22));	
		ts.add(new Student("wangwu",19));			
		ts.add(new Student("zhaoliu",22));		
				
		
		Iterator it=ts.iterator();
		while(it.hasNext())
		{
			Student s=(Student)it.next();
			sop(s.getName()+"...."+s.getAge());
		}
		
	}
	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
}

//方式二
class MyCompare implements Comparator     //创建一个比较器,实现Comparator接口
{
	//复写Comparator接口中的compare方法
	public int compare(Object o1,Object o2)												{
		if(!((o1 instanceof Student)&&(o2 instanceof Student)))
			throw new RuntimeException("不是学生对象");
		Student s1=(Student)o1;
		Student s2=(Student)o2;
		
		int num=s1.getName().compareTo(s2.getName());
		if(num==0)
			return new Integer(s1.getAge()).compareTo(new Integer(s2.getAge()));
		return num;
	}
}



//方式一
class Student implements Comparable  //该接口强制让学生类具备比较性
{
	private String name;
	private int age;
	public String getName()
	{
		return this.name;
	}
	public int getAge()
	{
		return this.age;
	}
	
	Student(String name,int age)
	{
		this.name=name;
		this.age=age;
	}
	
	//复写Comparable接口中的compareTo方法
	public int compareTo(Object obj)
	{
		/*
		当这个方法只有一句代码:return 1  ,则是按添加顺序排序
		若return -1,则按添加顺序倒序排序
		若返回0,则只有一个元素
		*/
		if(!(obj instanceof Student))
			throw new RuntimeException("不是Student类型");
		Student s=(Student)obj;
		if(this.age>s.age)
			return 1;
					
		if(this.age==s.age)
			//return 0;
			return this.name.compareTo(s.name);
		return -1;
	}
	
}

从上面的例子中可以看出,方式一是通过实现 comparable接口,复写 compareTo方法来完成排序的,比较条件是按年龄大小,次要条件是按名字。
方式二是通过实现Comparator接口,复写compare方法来排序的。比较条件是按名字自然顺序来的,次要条件是按年龄大小的。看结果:

从结果可以看出,当两种排序都存在时,以比较器为主。

下面来个小练习:
/*
练习:按照字符串长度排序
*/
import java.util.*;
class TreeSetTest
{
	public static void main(String[] args)
	{
		TreeSet ts=new TreeSet(new myCompare());
		
		ts.add("asdf");
		ts.add("df");
		ts.add("fasdfaf");
		ts.add("dfs");
		ts.add("efs");
		
		Iterator it=ts.iterator();
		while(it.hasNext())
		{
			sop(it.next());
		}
	}
	// 打印
	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
}

//创建比较器,实现Comparator接口
class myCompare implements Comparator
{
	//复写compare方法
	public int compare(Object o1,Object o2)
	{
		//当对象不是字符串,抛出异常,使程序停止
		if(!((o1 instanceof String)&&(o2 instanceof String)))
			throw new RuntimeException("非字符串");
		String s1=(String)o1;
		String s2=(String)o2;
		int num=new Integer(s1.length()).compareTo(s2.length());
		if(num==0)  //当首要条件相等时,要判断次要条件
			return s1.compareTo(s2);
		return num;
	}
}

好了,集合几个实现类都说完了,现在总结一下。

集合最高父接口 Collection   ,其中的常用方法像 add(),remove(Object o),hashCode(),contains(Object o),equals(Object o),size()等,还有不太常用的clear(),isEmpty(),addAll()等,还有最重要的iterator 迭代器    既然是集合中的最高级别,那么这些方法下面几个子接口,实现类都是可以用的,要么直接使用,要么复写。

然后是List 接口,因为List集合是有序的,因为有索引,所以是可以重复的。
因为是索引,所以其特有的方法一般都是带有索引的。其中值得重点的的就是ListIterator   列表集合器。这个是List集合中特有的,所以List下面的实现类都可以使用。当然,Set集合下面的小弟(实现类)就只能干瞪眼了,乖乖使用Iterator了,当然,不是说没了迭代器我们就不能取集合中的元素了,别忘了还有for循环。
还有一个已经被ArrayList替代的类Vector,它获取集合中的元素的方式除了循环和迭代器外,还有一种就是枚举。其使用方式类似Iterator。


像List下的ArrayList和LinkedList两个类在判断是否包含contains和移除remove时,依赖的是equals方法。当我们需要自定义判断条件时,就需要复写equals 方法了。
而Set下的HashSet中,判断是否包含contains和移除remove时,依赖的则是hashCode和equals方法,通常我们需要将这两个方法进行复写(先比较哈希值,若哈希值一样,再通过equals比较对象)

当然,这个hashCode方法和equals方法都不需要我们去手动调用的,而是集合底层自动调用的。

Set下的另一个集合类  TreeSet,底层是二叉树。它能够对Set集合中的元素排序。排序方式有两种。
一、元素(对象所属的类)实现comparable接口,复写compareTo方法,使元素具备比较性。这种方式称为自然排序,也叫默认排序。
二、定义一个比较器(类),实现Comparator接口,然后复写接口中的compare方法。最后将比较器的对象作为参数传递给集合构造函数。第二种方式是在当元素自身不具备比较性或者具备的比较性不是所需要的时候使用。

当两种方式共存时,以比较器为主。好了,搞定!!!




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值