小码哥杯java程序比赛复习(十二)集合和泛型(上)

一.集合类

   Java中的集合类:是一种工具类,就像是容器,储存任意数量的具有共同属性的对象;

   集合的作用:

   1.在类的内部,对数据进行组织;

   2.简单而快速的搜索大数量的条目;

   3.有的集合接口,提供了一系列排列有序的元素,并且可以在序列中间快速的插入或者删除有关元素;

   4.有的集合接口,提供了映射关系,可以通过关键字(key)去快速查找到对应的唯一对象,而这个关键字可以是任意类型;

   与数组的对比--为何选择集合而不是数组

   1.数组的长度固定,集合长度可变;

   2.数组只能通过下标访问元素,类型固定,而有的集合可以通过任意类型查找所映射的具体对象;

   Java的集合类

   Java的集合类主要由两个接口派生而成:Collection和Map,Collection和Map是Java集合框架的根接口,这两个接口又包含了一些子接口或实现类.

   在Collection接口中,Set和List接口是Collection接口派生的两个子接口,它们分别代表了无序集合和有序集合;Queue是Java提供的队列实现,类似于List;

   而Map实现类用于保存具有映射关系的数据,Map岙村的每项数据都是key-value对(键值对),也就是由key和value两个值组成。比如课程表-01.java,每项课程由两个值组成,即课程号和课程名。Map与此类似,Map里的key是不可重复的,key用于标识集合里的每项数据,如果需要查阅Map中的数据时,总是根据Map的key来获取。

    由此我们可以把Java的所有集合分为三大类,其中Set集合类似于一个罐子,把一个对象添加到Set集合时,Set集合无法记住添加这个元素的顺序,所以Set里的元素不能重复(否则系统无法准确识别出这个元素);List集合非常像是一个数组,它可以记住每次添加元素的顺序,且list的长度可变。Map集合也像是一个罐子,只是它里面的每项数据都由两个值组成。

    而访问这三个集合中的元素时,List集合可以直接根据元素的索引来进行访问,Map集合可以根据每项元素的key值来访问其value,Set集合只能根据元素本身来进行访问(这也就是Set集合里元素不允许重复的原因)。

二.List集合

   List集合代表一个元素有序,可重复的集合,集合中的每个元素都有其对应的顺序索引。List集合允许使用重复元素,可以通过索引来访问指定位置的集合元素。List集合默认按元素的添加顺序设置元素的索引。

  下面我们通过一个案例实现功能,来具体了解List集合类是如何实现的:

   实现功能--学生选课

Course类:

package List;
/**
 * 课程类
 * 
 */
public class Course {
	private String id;
	private String name;
	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 Course(String id, String name) {
		this.id = id ;
		this.name = name;
	}
	public Course() {	
	}
}

Student类:

package List;
import java.util.HashSet;
import java.util.Set;
/**
 * 学生类
 *
 */
public class Student {
	private String id;
	private String name;
	
	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 Set<Course> getCourses() {
		return courses;
	}
	public void setCourses(Set<Course> courses) {
		this.courses = courses;
	}
	public Set<Course> courses;
	//提供一个含参数的构造器,只需要提供id和姓名,就可以创建一个新的学生
	public Student(String id, String name) {
		this.id = id;
		this.name = name;
		this.courses = new HashSet<Course>();
	}
}
ListTest类:
package List; 
import java.util.ArrayList;  //要引入util包下的List类
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
/**
 * 备选课程类
 * @author Administrator
 *
 */
public class ListTest {
	
	public List coursesToSelect;//用于存放备选课程的List
	
	public ListTest() {
		this.coursesToSelect = new ArrayList();
	}
	
	/**
	 * 用于往coursesToSelect中添加备选课程
	 */
	public void testAdd() {
		//创建一个课程对象,并通过调用add方法,添加到备选课程List中
		Course cr1 = new Course("1" , "数据结构");//创建一个课程对象的实例
		coursesToSelect.add(cr1);//第一种add方法的使用,通过其来添加元素
		//当对象被添加到集合中时,都会变成Object类型;这时取出时就需要进行类型转换
		Course temp = (Course) coursesToSelect.get(0);//通过get方法把index索引处的元素取出
		System.out.println("添加了课程:"+temp.getId()+":" +temp.getName());
		
		Course cr2 = new Course("2", "C语言");
		//第二种add方法的使用,不仅可以添加新元素,还可以指定要添加元素的位置
		coursesToSelect.add(0, cr2);//将其添加到队头,则原来队头的元素将后移
		Course temp2 = (Course) coursesToSelect.get(0);
		System.out.println("添加了课程:" + temp2.getId() + ":" + temp2.getName());
		
		coursesToSelect.add(cr1);
		Course temp0 = (Course) coursesToSelect.get(2);
		System.out.println("添加了课程:" + temp.getId() + ":" + temp.getName()); 
		
		/**
		 * 思考一个问题,在使用add方法的时候,传递进去的位置参数,如果大于现在的长度,那么会发生什么情况?
		 */
		//以下方法会抛出数组下标越界异常
//		Course cr3 = new Course("3", "test");
		//因为之前添加了cr1和cr2此时的长度为2
//		coursesToSelect.add(4, cr3);
		
		//创建一个课程数组
		Course[] course = {new Course("3", "离散数学"), new Course("4", "汇编语言")};
		//通过Arrays的asList方法,将刚刚创建的course数组转换为List对象
		//第一个addAll方法,将要添加的List对象作为参数
		coursesToSelect.addAll(Arrays.asList(course));
		Course temp3 = (Course) coursesToSelect.get(2);
		Course temp4 = (Course) coursesToSelect.get(3);
		
		System.out.println("添加了两门课程:" + temp3.getId() + ":" + 
				temp3.getName() + ";" + temp4.getId() + ":" + temp4.getName());
		//第二个addAll方法,将要添加的List对象和添加的位置作为参数
		Course[] course2 = {new Course("5", "高等数学"), new Course("6", "大学英语")};
		coursesToSelect.addAll(2, Arrays.asList(course2));
		Course temp5 = (Course) coursesToSelect.get(2);
		Course temp6 = (Course) coursesToSelect.get(3);
		System.out.println("添加了两门课程:" + temp5.getId() + ":" + 
				temp5.getName() + ";" + temp6.getId() + ":" + temp6.getName());
		
	}
	
	/**
	 * 通过get取得List中的元素的方法
	 * @param args
	 */
	public void testGet() {
		int size = coursesToSelect.size();
		System.out.println("有如下课程待选:");
		for(int i= 0 ; i < size; i++) {
			Course cr = (Course) coursesToSelect.get(i);
			System.out.println("课程:" + cr.getId() + ":" + cr.getName());
		}
	}
	
	/**
	 * 通过迭代器来遍历List
	 * 在Collection接口中,定义了一个Iterator的方法,通过这个方法,可以返回一个当前迭代对象的迭代器,
	 * 再通过这个迭代器来返回当集合中的每一个元素
	 * 需要说明的是:迭代器只是用来遍历集合中的元素的,其本身不具备任何存储元素的功能,
	 * 也可以说迭代器是依附于某个集合而存在的,其本身不能够独立存在
	 */
	public void testIterator() {
		//通过集合的iterator方法,取得迭代器的实例
		Iterator<Course> it = coursesToSelect.iterator();
		System.out.println("有如下课程待选(通过迭代器访问):");
		while(it.hasNext()) {
			Course cr = (Course)it.next();//同样也需要进行类型强转 
			System.out.println("课程:"+cr.getId()+ ":" + cr.getName());
		}
	}
	
	/**
	 * 通过for each方法访问集合元素
	 * 其实就是迭代器的一种简便的写法
	 */
	public void testForEach() {
		System.out.println("有如下课程待选(通过for each访问):");
		for (Object obj : coursesToSelect) {//集合存进去时,都是以Object类型存进去的
			Course cr = (Course) obj;  //取出时,需要对Object类型进行强制转换
			System.out.println("课程:" + cr.getId() + ":" + cr.getName());
		}
	}
	/**
	 * 通过set方法修改List中的元素
	 * set方法需要两个参数,一个是索引位置的值,另一个是将要修改的新对象的值
	 */
	public void testModify() {
		coursesToSelect.set(4, new Course("7", "毛概"));
	}
	/**
	 * 通过remove方法删除List中的元素
	 * @param args
	 */
	public void testRemove() {
		/**
		 * 第一个remove方法是通过:取得某个位置上的元素,
		 * 然后将该元素传递到remove方法中从而实现删除
		 */
		Course cr = (Course) coursesToSelect.get(4);
		System.out.println("我是课程:"+cr.getId()+":"+cr.getName()+",我即将被删除");
		coursesToSelect.remove(cr);
		System.out.println("成功删除课程!");
		testForEach();
		/**
		 * 第二个remove方法是:根据索引位置来进行删除
		 */
		System.out.println("即将删除4位置上的课程!");
		coursesToSelect.remove(4);
		System.out.println("成功删除课程!");
		testForEach();
	    /**
	     * 第三个removeall方法是:从一个集合中通过removeall方法将另一个集合中的元素删除
	     */
		System.out.println("即将删除4位置和5位置上的课程!");
		//先新建一个coures数组
		Course[] courses = {(Course) coursesToSelect.get(4), (Course) coursesToSelect.get(5)};
		//其参数必须为集合
		coursesToSelect.removeAll(Arrays.asList(courses));
		
		System.out.println("成功删除课程!");
		testForEach();
	}
	public static void main( String[] args) {
		ListTest lt = new ListTest(); //先创建一个ListTest对象的实例
		lt.testAdd();
		lt.testGet();
		lt.testIterator();
		lt.testForEach();
		lt.testModify();
		lt.testForEach();
		lt.testRemove();
	}
}

   

    下面,我们一起来思考一个问题:

   在前面的add方法中,我们添加的都是课程类型的对象,而在取出的方法中,我们也是将取出的元素强转为课程类型,来继续向下操作的,那么如果我们在添加的时候,添加的不是课程类型的对象,会出现什么情况呢?如下所示:

   

/**
	 * 往List中添加一些奇怪的东西
	 * 看能否添加成功
	 * String类型无法强转为Course类型
	 */
	public void testType() {
		System.out.println("能否往List中添加一些奇怪的东西呢!?");
		coursesToSelect.add("我不是课程,我只是一个无辜的字符串!!");
	}

    经过测试,在向集合中添加String字符串,会抛出异常,那么问题来了,有没有可以控制向集合中添加元素类型的方法呢?

    这时候,就需要用到泛型的知识了:

泛型:  

    集合中的元素,可以是任意类型的对象(对象的引用):

    如果把某个对象放入集合,则会忽略它的类型,把其当做object类型处理。

    但使用泛型则可以规定某个集合只可以存放特点类型的对象,其会完全记住集合中元素的类型,并可以在编译时检查集合中元素的类型,如果试图向集合中添加不满足类型要求的对象,编译器就会提示错误。

注意:

1.泛型集合中,不能添加泛型规定的类型及其子类型以外的对象,否则会报错;

2.泛型集合的限定类型不能使用基本数据类型,但可以使用基本数据类型相对应的包装类来限定允许存入的基本数据类型;

泛型集合:

package List;
import java.util.ArrayList;
import java.util.List;

public class TestGeneric {

	/**
	 * 带有泛型——Course,的List类型属性
	 */
	public List<Course> courses; //尖括号里规定要存放的的集合元素的类型
	
	public TestGeneric() {
		 //在构造器中初始化courses属性,
		//因为courses是带有泛型的集合,所以将其实例化时,在ArrayList中也是要加上泛型
		this.courses = new ArrayList<Course>();
	}
	
	/**
	 * 测试添加
	 */
	public void testAdd() {
		Course cr1 = new Course("1","大学语文");
		courses.add(cr1);
		//泛型集合中,不能添加泛型规定的类型及其子类型以外的对象,否则会报错!
//		courses.add("能否添加一些奇怪的东西呢??");
		Course cr2 = new Course("2","Java基础");
		courses.add(cr2);
	}
	
	/**
	 * 测试循环遍历
	 */
	public void testForEach() {
		//这里的foreach和上一个不一样,这里已经规定了泛型,不必再通过object类型取出再进行强制转换
		for (Course cr : courses) {
			System.out.println(cr.getId()+ ":" + cr.getName());
		}
	}
	
	/**
	 * 泛型集合可以添加泛型的子类型的对象实例
	 */
	public void testChild() {
		ChildCourse ccr = new ChildCourse();
		ccr.setId("3");
		ccr.setName("我是子类型的课程对象实例~~");
		courses.add(ccr);
	}
	
	/**
	 * 泛型不能使用基本类型
	 */
	public void testBasicType() {
		List<Integer> list = new ArrayList<Integer>();
		list.add(1);//基本类型被强制转换成了包装类型integer
		System.out.println("基本类型必须使用包装类作为泛型!" + list.get(0));
	}
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		TestGeneric tg = new TestGeneric();
		tg.testAdd();
		tg.testForEach();
		tg.testChild();
		tg.testForEach();
		tg.testBasicType();
	}

}

     楼主在这里只是分享下泛型在集合中的运用,至于泛型在枚举类,反射等方面的功能的应用,楼主将在以后的学习中与大家分享,欢迎拍砖!


猜你喜欢

转载自blog.csdn.net/qq_29027865/article/details/53020350
今日推荐