------- <a href="http://www.itheima.com" target="blank">android培训</a>、<a href="http://www.itheima.com" target="blank">java培训</a>、期待与您交流! ----------
1.前言:
(1)为什么会出现集合类:
面向对象对事物的体现都是以对象的形式体现的,所以为了方便对多个对象的操作,就对对象进行存储,集合就是存储对象最常用的的一种方式。
(2)数组和集合类的区别:
数组虽然也可以存储对象,但长度是固定的;集合长度是可变的。数组中可以存储基本数据类型,集合只能储对象。
(3)集合类的特点:
1、集合只用于存储对象。
2、集合长度是可变的。
3、集合可以存储不同类型的对象。
2.Collection接口:
1、List接口:可存放重复元素,元素存取是有序的。
2、Set接口:不可以存放重复元素,元素存取是无序的。
add方法的参数类型是Object,以便于接受任意类型对象。
集合中存储的都是对象的引用(地址)。
3.Collection的共性方法:
添加 add()
删除 remove()
修改 clear()
查询 iterator()
判断 contains()
4.List集合:
(1)List集合的子类:
1、ArrayList
底层的数据结构是数组结构,线程同步,默认的长度是10,查找速度快。
2、LinkedList
底层的数据结构是链表结构,查找速度慢,因为只知道相邻的元素,增删速度快。
3、Vector
底层的数据结构是数组结构,也是线程同步的,被ArrayList替代了。
(2)List集合中的方法:
1、添加:add(index,element); //往某个位置添加元素。
addAll(index,Collection);//往某个位置添加集合,添加一堆元素。
2、删除:remove(index); //根据位置删除元素。
3、修改:set(index,element); //修改某个位置上的元素。
4、查询:get(index); //获取位置上某个元素。
subList(from,to); //根据位置获取子列表。
listIterator(); //迭代器。
int indexOf(obj); //获取指定元素的位置。
ListIterator listIterator(); //列表迭代器。
(3)接口:ListIterator:
该接口只能通过List集合的listIterator方法获取,List集合特有的迭代器。ListIterator是Iterator的子接口。在迭代时,不可以通过集合对象的方法操作集合中的元素。因为会发生ConcurrentModificationException异常,也就是并发异常。
所以,在迭代时,只能用迭代器的方法操作元素,可是Iterator方法是有限的,只能对元素进行判断,取出,删除的操作,如果想要其他的操作如添加,修改等,就需要使用其子在迭代过程中,准备添加或者删除元素
5.ArraryList:
代码例子:
(1)
import java.util.*;
class ListDemo1
{
public static void main(String[] args)
{
method_1();
}
public static void method_1()
{
ArrayList a = new ArrayList();
a.add("java02");
a.add("java07");
a.add("java03");
a.add("java04");
a.add("java06");
a.add(1,"java08"); //从角标1的位置上添加"java08"。
a.remove(2); //删除角标2位置上对应的元素。
a.set(3,"java09"); //把角标位3对应的元素换成"java09"。
System.out.println(a.get(3)); //获取角标3位置上的元素。
System.out.println(a.subList(2,4);//从角标位2到4获取子列表。
System.out.println(a.indexOf("java03"); //获取"java03"这个元素对应的位置。
Iterator it =a.iterator(); //通过迭代器取出元素。
while(it.hasNext()) //普通的迭代器只具备:取出,判断,和删除的功能。
{
Object o =it.next(); //这里需要强转,因为不知道取什么类型的元素。
if(s.equals("java03")) //判断取出的元素中是否有"java03"这个元素。
it.remove(); //如果"java03"这个元素存在就将其删除。
}
}
}
(2)如果换成列表迭代器:
ListIterator<String> it =a.listIterator();
while(it.hasNext())
{
Object o =it.next();
if(s.equals("java03")) //判断取出元素当中是否存在"java55"这个元素。
it.set("java55"); //如果取出的元素中"java03"这个元素存在,就将其修
//改成"java55"。
else
it.add("java88");//如果"java55"这个元素不存在,就添加元素"java88"。
}
(3)去除ArrayList中的重复元素:
思路:定义一个功能,把ArrayList传给这个功能,然后这个功能返回元素唯一的ArrayList。
代码例子:
import java.util.*;
class ArrayListTest
{
public atatic void main(String[] args)
{
ArrayList al = new ArrayList(); //定义一个集合,存入几对相同的元素。
al.add("java01");
al.add("java02");
al.add("java01");
al.add("java02");
al.add("java03");
al = singleElement(al); //接收返回来的容器。
System.out.println(al); //打印结果为[java01,java02,java03]。
}
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; //返回一个临时容器。
}
}
(4)将自定义对象作为元素存到ArrayList集合中,并去除重复元素:
代码例子:
import java.util.*;
class Person //建立一个描述人的类。
{
private String name;
private int age;
Person(String name,int age)
{
this.name= name;
this.age= age;
}
public void setName(String name)
{
this.name=name;
}
public String getName()
{
return name;
}
public void setAge(int age)
{
this.age=age;
}
public int getAge()
{
return age;
}
public boolean equals(Object obj) //需要复写equals方法,因为Object中的
{ //equals方法判断的是对象的地址值是否
if(!(obj instanceof Person)) //相同,我们需要建立自己的判断的方法。
return false;
Person p =(Person)obj;
return this.name.equals(p.name) && this.age== p.age;
}
}
class ArrayLisTest
{
public static void main(String[] args)
{
ArrayList al = new ArrayList();
al.add(new Person("lisi",30));
al.add(new Person("zhagnsan",20));
al.add(new Person("lisi",30));
al.add(new Person("wangwu",25));
al = singleElement(al);
Iterator it = al.iterator();
while(it.hasNext()) //遍历返回的集合中的元素。
{
Object obj = it.next();
Person p =(Person)obj;
System.out.println(p.getName()+"..."+p.getAge());
}
}
public static ArrayList singleElement(ArrayList al)//定义一个功能去除重复元素。
{
ArrayList newAl =new ArrayList();
Iterator it = al.iterator();
while(it.hasNext())
{
Object obj = it.next();
Person p =(Person)obj;
if(!newAl.contains(p))
newAl.add(p);
}
return newAl;
}
}
总结:List集合判断元素是否相同,依据的是元素的equals方法。
6.LinkedList:
(1) LinkedList的特有方法:
1、addFirst(); //总是从开头增加。
2、addLast(); //总是从结尾增加。
3、getFirst(); //总是获取第一个位置的元素,获取元素,但不删除元素。如果集合中没有元素,则抛出异常。
4、getLast(); //总是获取最后一个位置的元素,获取元素,但不删除元素。如果集合中没有元素,则抛出异常。
5、removeFirst(); //总是获取第一个位置的元素,获取元素,并且删除元素。如果集合中没有元素,则抛出异常。
6、removeLast(); //总是获取最后一个位置的元素,获取元素,并且删除元素。如果集合中没有元素,则抛出异常。
代码例子:
import java.util.*;
class LinkedListDemo
{
Public static void main(String[] args)
{
LinkedList link = new LinkedList();
link.addFirst("java01");
link.addFirst("java02");
link.addFirst("java03");
System.out.println(link);//打印的结果为[java03,java02,java01]
//因为总是添加头。
//如果用addLast()来添加,那么结果是[java01,java02,java03]
//因为总是添加最后一位。
while(!link=isEmpty( ))
{
System.out.println(link.removeLast());//当集合不为空时,取一个删一个。
}
System.out.println(link.getFirst()); //打印结果为 java03
System.out.println(link.getFirst()); //打印结果为 java03,因为总是取第一个。
}
}
(2)JDK1.6出现了替代方法:
1、offerFirst(); 替代了 addFirst(); //总是从开头增加。
2、offerLast(); 替代了 addLast(); //总是从结尾增加。
3、peekFirst(); 替代了 getFirst(); //总是获取第一个位置的元素。
4、peekLast(); 替代了 getLast(); //总是获取最后一个位置的元素。
5、pollFirst(); 替代了 removeFirst(); //总是获取第一个位置的元素。
6、pollLast(); 替代了 removeLast();//总是获取最后一个位置的元素。
(3)使用LinkedList 模拟一个队列数据结构:
Import java.util.*;
class DuiLie
{
private LinkedList link;
DuiLie()
{
Link = new LinkedList();
}
public void myAdd()
{
link.addtFirst();
}
public Obiect myGet()
{
return link.getFirst();
}
public boolean isNull()
{
return link.isEmpty();
}
}
class LinkedListTest
{
public static void mian(String[] args)
{
DuiLie d = new DuiLie();
d.myAdd("java01");
d.myAdd("java02");
d.myAdd("java03");
while(!d.isNull())
{
System.out.println(d.myGet());//打印结果为[java01,java02,java03],
} //先进先出。
}
}
为什么要集合和方法封装起来?
直接用LinkedList是可以完成的,但是LinkedList只有自身的含义,叫做链表,如果我们想把它做成我们项目相关的一些容器,我们要命名一些特定的名称来使用,那么这个时候我们就将原有的结构封装到我们自己的描述当中,并对外提供一个方便我们识别的名称。
7.set集合:
(1)set集合的子类:
1、HashSet
底层的数据结构是哈希表,线程是非同步的。
2、TreeSet
底层的数据结构是二叉树。
(2)Set集合的方法:
Set集合和collection的功能是一致的。
8.HashSet
(1)HashSet保证元素的唯一性:
1、是通过元素的两个方法,hashCode和equals来完成。
2、如果元素的HashCode值相同,才会判断equals是否为true。
3、如果元素的hashcode值不同,不会调用equals。
总结:对于判断元素是否存在,以及删除等操作,依赖的方法是元素的hashcode
和equals方法。
练习:往hashSet集合中存入自定对象姓名和年龄相同为同一个人,重复元素。
代码例子:
import java.util.*;
class Person //建立一个描述人的类。
{
private String name;
private int age;
Person(String name,int age)
{
this.name= name;
this.age= age;
}
public void setName(String name)
{
this.name=name;
}
public String getName()
{
return name;
}
public void setAge(int age)
{
this.age=age;
}
public int getAge()
{
return age;
}
public boolean equals(Object obj) //需要复写equals方法。
{
if(!(obj instanceof Person))
return false;
Person p =(Person)obj;
return this.name.equals(p.name) && this.age== p.age;
}
public int hashCode() //复写hashcode方法。
{
System.out.println(this.name+"....hashCode");
return name.hashCode()+age*37;
}
}
class ArrayLisTest
{
public static void main(String[] args)
{
HashSet hs = new HashSet(); //定义一个HashSet 集合。
//向这个集合中添加自定义元素
hs.add(new Person("a1",11));
hs.add(new Person("a2",12));
hs.add(new Person("a3",13));
Iterator it = hs.iterator();
while(it.hasNext())
{
Object obj = it.next();
Person p =(Person)obj;
System.out.println(p.getName()+"..."+p.getAge());
}
}
}
HashSet是如何保证元素的唯一性的?
是通过元素的两个方法,hashcode和equals来完成的。如果元素的hashcode值相同,才会判断equals是否为true, 如果元素的hashcode值不同,不会调用equals。
9.TreeSet
(1)TreeSet可以对Set集合进行排序,第一种方式,让元素具备比较性。
让元素自身具备比较性,元素要实现Comparable接口,复写compareTo方法,比较的是元素的自然顺序。
如果想用TreeSet集合删除元素,或者是判断元素是否包含,都得用到compareTo
方法,返回0相同,返回1或者-1就不相同。
代码例子:
import java.util.*;
class Student implements Comparable// 实现Comparable接口。
{
private String name;
private int age;
Student(String name,int age)
{
this.name=name;
this.age=age;
}
public void setName(String name)
{
this.name=name;
}
public String getName()
{
return name;
}
public void setAge(int age)
{
this.age=age;
}
public int getAge()
{
return age;
}
public int compareTo(Object obj)//复写compareTo方法。
{ //让元素具备比较性。
if(!(obj instanceof Student))
throw new RuntimeException("不是学生对象");
Student s= (Student)obj;
if(this.age>s.age)
return 1;
if(this.age==s.age) //当元素的年龄相同时,就按照姓名排。
return this.name.compareTo(s.name);
return -1;
}
}
class TreeSetDemo
{
public static void main(String[] args)
{
TreeSet tree =new TreeSet();
tree.add(new Student("lisi02",22));
tree.add(new Student("lisi007",20));
tree.add(new Student("lisi09",19));
tree.add(new Student("lisi01",40));
Iterator t = tree.iterator();
while(t.hasNext())
{
Student s=(Student)t.next();
System.out.println(s.getName()+"....."+s.getAge());
}
System.out.println("Hello World!");
}
}
打印结果为:
lisi09",19
lisi007",20
lisi02",22
lisi01",40
(2)排序第二种方式,让集合具备比较性。
当元素不具备比较性,或者具备的比较性不是所需要的,这时就需要让集合自身具备比较性。
练习:按照字符串长度排序
代码:
import java.util.*;
class TreeSetTest
{
public static void main(String[] args)
{
//往集合中传入实现Comparator的子类对象。
TreeSet tree = new TreeSet(new StringLengthCompare());
tree.add("abcd");
tree.add("cc");
tree.add("cba");
tree.add("z");
tree.add("hahaha");
Iterator it = tree.iterator();
while(it.hasNext())
{
String s =(String)it.next();
System.out.println(s);
}
}
}
//实现Comparator接口。
class StringLengthCompare implements Comparator
{
//复写compare方法。
public int compare(Object obj1,Object obj2)
{
String s1=(String)obj1;
String s2=(String)obj2;
//比较元素的长度。
int num =new Integer(s1.length()).compareTo(new Integer(s2.length()));
if(num==0)
//当集合中出现长度相同的元素,那么就需要比较元素的自然顺序。
return s1.compareTo(s2);
return num;
}
}
打印结果为:
z
cc
cba
abcd
hahaha
总结:二叉树也是以return 0 来判断元素是否相同。如果自然顺序和比较器这两种方式都存在,那么就以比较器为主。
10.泛型:
泛型:JDK1.5版本以后出现的新特性,用于解决安全问题,是一个安全机制。
(1)泛型出现前的例子:
import java.util.*;
class ArrayListTest1
{
public static void main(String[] args)
{
ArrayList al = new ArrayList();
//这里我们添加了3个字符串对象。
al.add("abc01");
al.add("abc02");
al.add("abc03");
// al.add(8); //这里我们添加了一个Integer对象。
Iterator it = al.iterator();
while(it.hasNext())
{
/*这里需要强转,我们知道存的是字符串类型的元素,但是迭代器不知道取的是什么类型的元素,所以需要强
转。*/
Object obj =it.next();
//等取到以后,我们再把取到的元素向下转型,转成我们存入的类型。
String s =(String)obj;
System.out.println(s);
}
}
}
发现:
(1)取的时候不知道取什么类型的元素,需要强制转换。
(2)如果我们在上面的代码中添加个al.add(8),然后编译的时候不会报错,运行的时候才会报错,因为在定义集合的时候并没有声明是什么类型的,所以有可能存成不同的类型的数据。
(2)泛型出现后的例子:
import java.util.*;
class ArrayListTest1
{
public static void main(String[] args)
{
ArrayList<String> al = new ArrayList<String>();
al.add("abc01");
al.add("abc02");
al.add("abc03");
al.add(8);
Iterator<String> it = al.iterator();
while(it.hasNext())
{
//Object obj =it.next();
String s =it.next();
System.out.println(s);
}
}
}
发现:
1、加上泛型以后,不用强制转换类型了,因为集合声明了类型,迭代器也声明
了和集合相同的类型。
2、当我们忘集合里面添加了一个元素al.add(8),这时候编译的时候就会报错,
因为集合已经声明了是String类型的,也就是只能添加String类型的元素,
添加了其它类型的元素就会报错。
(3)那么泛型的作用是什么呢?
1、将运行时期出现的问题class cast Exception,转移至编译时期,方便程序员
解决问题,让运行时期的问题减少,提高安全性。
2、避免了强制转换的麻烦。
(4)泛型的格式:
通过<> 来定义要操作的引用数据类型。
11.泛型类:
(1)定义工具类时思考:
我们要操作一个对象,操作哪一个呢?不知道,什么类型也不确定,那么就定义一个参数,对方在使用工具类的时候,由他来指定要操作什么类型的对象。
代码例子:定义一个工具类。
(2)泛型前做法:
cass Tool
{
private Object obj;
public void setObject(Object obj)
{
this.obj =obj;
}
public Object getObject()
{
return obj;
}
}
class GenericDemo
{
public static void main(String[] args)
{
Tool t = new Tool();
t.setObject(new student());//传进来的是学生对象。
worker w =(workert).getObject();//我们想接收的是worker。
}
}
通过上例发现:我们想通过这个工具类来描述工人,但是我们可能在传对象的时候写错,比如写成了学生,那么编译的时候不会报错,运行的时候就会报错。
(3)泛型后做法:
class Utils<QQ> //泛型类。
{
private QQ q;
public void setObject(QQ q)
{
this.q = q;
}
public QQ getObject()
{
return q;
}
}
class GenericDemo
{
public static void main(String[] args)
{
Utils<worker> u = new Utils<worker>();
u.setObject(new whorker); //如果写成new student(),编译会报错。
worker w = u.getObject(); //不需要强转了。
}
}
通过上例发现:
加上泛型以后,如果写成了学生对象,那么在编译时期就会报错,而且不需要强制转换了。
(4)那么什么时候定义泛型类呢?
当一个类中要操作的引用数据类型不确定的时候,以前是定义Object类完成,现在是定义泛型来完成。
(5)注意:如果是基本数据类型不确定的时候不能使用泛型。
12.泛型方法:
(1)泛型除了可以定义在类上,还可以定义在方法上。
(2)泛型类定义的泛型,在整个类中有效,如果被方法使用,那么泛型类的对象明确要操作的具体类型后,所有要操作的类型就已经固定了,所以为了让不同的方法可以操作部同类型,而且操作的类型不确定,那么可以将泛型定义在方法上。
(3)泛型定义在方法上,只能在功能中有效。
代码例子:
class Demo
{
public<T> void show(T t) //泛型定义在方法上,需要在语句中声明。
{
System.out.println("show:"+t);
}
public<Q> void print(Q q) //泛型定义在方法上,与上一个方法没有关系。
{
System.out.println("print=:"+q);
}
}
class GenericDemo
{
public static void main(String[] args)
{
Demo d = new Demo();
d.show("haha"); //往里面传什么类型的参数,就显示什么参数。
d.print(4); //往里面传什么类型的参数,就打印什么参数。
}
}
代码例子:
既可以有泛型类,也可以有泛型方法,包括静态方法。
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 <Y>void method(Y y);//泛型定义在静态方法上。
{
System.out.println(y);
}
}
class GenericDemo
{
public static void main(String[] args)
{
/*因为类上定义了泛型,所以在创建对象的时候需要指明操作的类型。
我们指定类型是String。*/
Demo<String> d = new Demo<String>();
d.show("haha");
/*show方法类型与类对象类型相同,也就是只能接收String类型的数据,
现在传了一个整数类型的数据,所以会报错。
d.show(8);
//print方法与类上定义的泛型无关,传什么类型的参数,打印什么参数。
d.print(4);
Demo.method("zhanghao");//静态方法用类名调用。
}
}
(4)注意:静态方法不可以访问类上定义的泛型,如果静态方法要操作的引用数
据类型不确定,可以将泛型定义在方法上。
13.泛型定义在接口上:
当接口不知道操作什么类型的数据时,可以将泛型定义在接口上。
代码例子:
interface Inter<T> //泛型定义在接口上。
{
void show(T t);
}
//一个类实现接口,用的时候传一个实际的参数。
class InterImpl implements Inter<String>
{
public void show(String s)
{
System.out.println(s);
}
}
class GenericDemo
{
public static void main(String[] args)
{
nterImpl i = new nterImpl();
i.show("hahah");
}
}
如果说实现的时候也不知道操作什么,那么实现的时候也是泛型。
代码例子:
class InterImpl implements Inter<T>
{
public void show(T t)
{
System.out.println(t);
}
}
class GenericDemo
{
public static void main(String[] args)
{
//在创建对象的时候指定泛型。
nterImpl<String> i = new nterImpl<String>();
i.show("hahah");
}
}
14.泛型限定:
(1)格式:
1、<? extends E>:可以接收E类型和E的子类型。
2、<? super E>:可以接收E类型和E的父类型。
3、<?> :通配符,也可以理解为占位符。意思就是具备泛型,但是操作什么类
型不知道,就占个位置,表示里面肯定有类型。
(2)练习:
代码例子:
import java.util.*;
class GenericDemo2
{
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"));
method(al);
ArrayList<Student> al1 = new ArrayList<Student>();
al1.add(new Student("abcd1"));
al1.add(new Student("abcd2"));
al1.add(new Student("abcd3"));
//method(al1);
}
//我们给这个方法定义的是Person类型的,所以当传Student的时候会报错。
public static void method(ArrayList<Person> al)
{
Iterator<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 void setName(String name)
{
this.name=name;
}
public String getName()
{
return name;
}
}
class Student extends Person
{
Student(String name)
{
super(name);
}
}
发现:
那么如果我们想让Person和Student同时调用method方法,怎么办?
那么就需要泛型限定。例如:把method方法里的参数改成是
public static void method(ArrayList<? extends Person> al),也就是,能接收Person类型和Person的子类型。如果再类一个worker也可以用这个方法。
<?>和<T>的区别:
<?>是一个占位符,意思就是占一个位置,里面肯定有类型,但是不知道什么类型,它不需要在语句中声明。
<T>里面代表的是一个具体的类型,它需要在语句中声明,而且传进去什么类型就是什么类型。
------- <a href="http://www.itheima.com" target="blank">android培训</a>、<a href="http://www.itheima.com" target="blank">java培训</a>、期待与您交流! ----------