集合
1.为什么会出现集合类?
因为面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储,集合就是存储对象最常用的一种方式。
2.数组和集合类同是容器,有何不同?
数组虽然也可以存储对象,但长度是固定的;集合长度是可变的。数组中可以存储基本数据类型,集合只能存储对象。
3. 集合类的特点:集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象。
4.集合框架的构成及分类。
5.为什么会出现这么多容器呢?
因为每一个容器对数据的存储方式都有不同。这个存储的方式称之为:数据结构。
集合中存放的不可能是对象实体,存放的都是地址。
6.什么是迭代器呢?其实就是集合的取出元素的方式。
就把取出方式定义在集合的内部,这样取出方式就可以直接访问集合内容的元素。那么取出方式就被定义成了内部类。而每个容器的数据结构不同,所以取 出的动作细节也不一样。但是都有共性内容判断和取出。那么可以将写共性抽取。那么这些内部类都符合一个规则。该规则就是Iterator。如何获取集合的取 出对象?通过一个对外提供的方法。iterator();
7.常见子接口:
collection
|------List:元素是有序的,元素可以重复。因为该集合体系有索引。由于底层数据结构不同,有三个子类。
|--ArrayList:底层数据结构使用的是数组结构。特点:查询速度快,但增删稍慢。线程不同步(建议使用)。
|--LinkedList:底层的数据结构是链表数据结构。特点:增删的速度快,查询稍慢。
|--Vector:底层是数组数据结构。线程同步。
ArrayList默认长度是10,超出长度时,50%延长。
Vector默认长度是10,超出长度时,100%延长。
|------Set:元素是无序的,元素不可以重复。该集合体系无索引。Set集合的功能和Collection是一致的。
|--HashSet:底层数据结构是哈希表。线程是非同步的。
|--TreeSet:可以对Set集合中的元素进行排序。按照字母的自然顺序排序。底层数据结构是二叉树,保证元素唯一性的依据是compareTo方法return 0。
8.collection集合的功能,代码示例。
代码(一)
import java.util.*;
/*
1.add方法的参数类型是Object,以便于接受任意的类型对象。
2.集合中存储的都是对象的引用(地址)。
*/
class CollectionDemo
{
public static void main(String[] args)
{
//创建一个集合容器,使用Collection接口的子类,ArrayList
ArrayList a1 = new ArrayList();
//1.添加元素
a1.add("java01");
a1.add("java02");
a1.add("java03");
a1.add("java04");
//打印集合
//sop("原集合:"+a1);//直接将对象封装在中括号中,打印出来
//2.获取个数,集合长度。
//sop("size:"+a1.size());
//3.删除元素
//a1.remove("java02");
sop(a1);
//4.清空集合
//a1.clear();
//5.判断元素。
sop("java03是否存在:"+a1.contains("java03"));
sop("集合是否为空?"+a1.isEmpty());
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
代码(二)
import java.util.*;
class CollectionDemo2
{
public static void main(String[] args)
{
//method_1();
method_get();
}
//取交集。
public static void method_1()
{
ArrayList a1 = new ArrayList();
a1.add("java01");
a1.add("java02");
a1.add("java03");
a1.add("java04");
ArrayList a2 = new ArrayList();
a2.add("java03");
a2.add("java04");
a2.add("java05");
a2.add("java06");
//a1.retainAll(a2);//取交集,a1中只会保留和a2中相同的元素,
//若没有交集,存空。
a1.removeAll(a2);//将相同的元素删除。a1中只保存不同。
sop("a1="+a1);
sop("a2="+a2);
}
//取出元素
public static void method_get()
{
//添加元素
ArrayList a1 = new ArrayList();
a1.add("java01");
a1.add("java02");
a1.add("java03");
a1.add("java04");
/*Iterator it = a1.iterator();//获取迭代器,用于取出集合中的元素。迭代结束,it还在内存中。
while(it.hasNext())
{
sop(it.next());
}*/
for (Iterator it = a1.iterator(); it.hasNext() ; )//迭代结束,释放内存it
sop(it.next());
}
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
9.List常见方法
特有方法:凡是可以操作角标的方法都是该体系特有的的方法。
增:
add(index,element)//在指定位置添加元素。
addAll(index,Collection)//将指定 collection 中的所有元素都插入到列表中的指定位置
删:
remove(index)//删除指定位置的元素。
改:
set(index,element)//修改指定位置的元素
查:
get(index)//返回列表中指定位置的元素
subList(from,to)//返回列表中指定的from(包括 )和 to(不包括)之间的部分视图。
listIterator()// 返回按适当顺序在列表的元素上进行迭代的迭代器。
List集合特有的迭代器.ListIterators是Iterator的子接口。
在迭代时,不可以通过集合对象的方法操作集合中的元素。
因为会发生ConcurrentModificationException异常。
所以,在迭代器时,只能用迭代器的方法操作元素,可是Iterator方法是有限的。
只能对元素进行判读,取出,删除的操作,
如果想要其他的操作如添加,修改等,就需要使用其子接口,ListIterator。
该接口只能通过List集合的listIterator方法获取。
ArrayList代码示例:
/*
将自定义对象作为元素存到ArrayList集合中,并去除重复元素。
比如:存入对象。同姓名同年龄,视为同一个人。为重复元素
思路:
1.对人描述,将数据封装在人对象。
2.定义容器,将人存入。
3.取出。
List集合判断元素是否相同,依据的是元素的equals方法。
*/
import java.util.*;
class Person
{
private String name;
private int age;
Person(String name,int age)
{
this.name = name;
this.age = age;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
public boolean equals(Object obj)//重写equals方法。
{
if (!(obj instanceof Person))
{
return false;
}
Person p = (Person)obj;//类型转换。
System.out.println(this.name+"::"+ p.name);
return this.name.equals(p.name) && this.age == p.age;
}
}
class ArrayListTest2
{
public static void main(String[] args)
{
ArrayList al = new ArrayList();
al.add(new Person("lisi01",20));//al.add(Object obj)//Oject obj=new Person()类型提升
al.add(new Person("lisi01",20));
al.add(new Person("lisi02",40));
al.add(new Person("lisi02",40));
al.add(new Person("lisi03",35));
al.add(new Person("lisi03",35));
al = singleElement(al);//调用函数,去除相同元素,
Iterator it = al.iterator();//定义的迭代器
while (it.hasNext())
{
/*Object obj = it.next();//
Person p = (Person)obj;//类型转换。*/
Person p = (Person)it.next();
sop(p.getName()+"......"+p.getAge());
}
}
public static ArrayList singleElement(ArrayList al)
{
//定义一个临时容器
ArrayList newAl = new ArrayList();
//定义迭代器
Iterator it = al.iterator();
while (it.hasNext())
{
//定义一个obj对象
Object obj = it.next();
//当临时容器newAl不包含obj对象时,向临时容器传入obj对象。
if(!newAl.contains(obj))//调用contains,contains调用equals,比较对象。
newAl.add(obj);
}
return newAl;
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
10.LinkedList结构:
LinkedList特有方法:
addFirst():
addLast():
getFirst():
getLast():获取元素,但不删除元素。如果集合中没有元素会出现NoSuchElementException。
removeFisrt():
removerLast():获取元素,删除元素。如果集合中没有元素会出现NoSuchElementException。
在JDK1.6之后,出现了替代方法。
offerFirst();
offerLast();
peekFirst();
peekLast();获取元素,但不删除元素,如果集合中没有元素,会返回null。
pollFirst();
pollLast();获取元素,但是元素删除,如果集合中没有元素,会返回null。
LinkedList代码示例
/*11.Vector取出方式:迭代器,遍历(for循环),索引,枚举。枚举是Vector特有的方式。
使用LinkedList模拟一个堆栈或者队列数据结构。
堆栈:先进后出。
队列:先进先出。
*/
import java.util.*;
class DuiLie
{
private LinkedList link;
DuiLie()
{
link = new LinkedList();
}
public void myAdd(Object obj)
{
link.offerFirst(obj);
}
public Object get_1()
{
return link.pollFirst();
}
public Object get_2()
{
return link.pollLast();
}
public boolean isNull()
{
return link.isEmpty();
}
}
class LinkedTextDemo1
{
public static void main(String[] args)
{
DuiLie d = new DuiLie();
d.myAdd("java01");
d.myAdd("java02");
d.myAdd("java03");
d.myAdd("java04");
while (!d.isNull())
{
sop(d.get_1());// 模拟堆栈,先进后出
//sop(d.get_2())//模拟队列,先进先出。
}
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
发现枚举和迭代器很像,其实枚举和迭代时一样的。因为枚举的名称以及方法的名称都过长。所以被迭代器取代了。
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());
}
}
}
12.Set集合
哈希表添加元素的返回值是boolean型,因为要判断地址和对象是否相同
(1) HashSet如何保证元素的唯一性呢?是通过元素的两个方法,hashCode和equals来完成。
如果元素的HashCode值相同,才会判断equals是否为true。如果元素的HashCode值不同,不会调用equals。
注意:对于判断元素是否存在,以及删除等操作,依赖元素的HashCode和equals方法。
(2)TreeSet中按照排序判断用了compareTo()方法。
记住,在排序时,当主要条件相同时,一定要判断一下次要条件。
TreeSet排序的第一种方式:让元素自身具备比较性。元素需要实现comparable接口,覆盖compareTo方法,这种方式也称为元素的自然顺序,或者叫做默认顺序。
TreeSet的第二种排序方式:当元素自身不具备比较性时,或者具备的比较性不是所需要的。这时就需要让集合自身具备比较性。在集合初始化时就有了比较方式。
定义了比较器,将比较器对象作为参数传递给TreeSet集合的构造函数。
代码示例(HashSet):
import java.util.*;
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;
}
public int hashCode()//判断哈希地址
{
System.out.println(this.name+"...hashCode");
return name.hashCode()+age;
}
public boolean equals(Object obj)//判断对象
{
if(!(obj instanceof Person))
{
return false;
}
Person p = (Person)obj;//类型转换
System.out.println(this.name+"...."+p.name);
return this.name.equals(p.name) && this.age == p.age;
}
}
class HashCodeTest
{
public static void main(String[] args)
{
HashSet hs = new HashSet();
hs.add(new Person("lisi01",10));
hs.add(new Person("lisi02",11));
hs.add(new Person("lisi03",12));
hs.add(new Person("lisi01",10));
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);
}
}
代码示例(TreeSet)
/*
需求:往TreeSet集合中存储自定义对象学生。
想按照学生的年龄进行排序。
分析:我们输入学生的对象,不具备比较性,TreeSet调用compareTo方法对存储的数据进行排序。
我们存入的数据是一个对象,所以要重写compareTo方法,
让其可以处理对象。
*/
import java.util.*;
class Person implements Comparable//该接口强制让人具备比较性
{
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;
}
public int compareTo(Object obj)
{
if (!(obj instanceof Person))
throw new RuntimeException("不是人的对象");
Person p = (Person)obj;
if(this.age > p.age)
return 1;
if(this.age == p.age)
return this.name.compareTo(p.name);//年龄相同时,比较姓名。
return -1;
}
}
class TreeSetDemo
{
public static void main(String[] args)
{
TreeSet ts = new TreeSet();
ts.add(new Person("lisi01",19));
ts.add(new Person("lisi02",20));
ts.add(new Person("lisi03",21));
ts.add(new Person("lisi02",19));
Iterator it = ts.iterator();
while (it.hasNext())
{
Person p = (Person)it.next();
sop(p.getName()+"::"+p.getAge());
}
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
/*
当元素自身不具备比较性,或者具备的比较性不是所需要的,
这是需要让容器自身具备比较性。
定义了比较器,将比较器对象作为参数传递给TreeSet集合的构造函数。
当两种排序都存在时,以比较器为主。
定义一个类,实现Comparator接口,覆盖compare方法。
*/
import java.util.*;
class Person implements Comparable//该接口强制让人具备比较性
{
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;
}
public int compareTo(Object obj)
{
if (!(obj instanceof Person))
throw new RuntimeException("不是人的对象");
Person p = (Person)obj;
if(this.age > p.age)
return 1;
if(this.age == p.age)
return this.name.compareTo(p.name);//年龄相同时,比较姓名。
return -1;
}
}
class TreeSetDemo2
{
public static void main(String[] args)
{
TreeSet ts = new TreeSet(new MyCompare());
ts.add(new Person("lisi01",19));
ts.add(new Person("lisi02",20));
ts.add(new Person("lisi03",21));
ts.add(new Person("lisi02",19));
Iterator it = ts.iterator();
while (it.hasNext())
{
Person p = (Person)it.next();
sop(p.getName()+"::"+p.getAge());
}
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
class MyCompare implements Comparator//定义一个比较器的类。
{
public int compare(Object o1,Object o2)
{
Person p1 = (Person)o1;
Person p2 = (Person)o2;
int num = p1.getName().compareTo(p2.getName());
if (num == 0)
{
//将年龄封装为对象,进行比较。
return new Integer(p1.getAge()).compareTo(new Integer(p2.getAge()));
/*if (p1.getAge() > p2.getAge())
return 1;
if (p1.getAge() < p2.getAge())
return -1;
return 0;*/
}
return num;
}
}
13.泛型。
泛型:JDK1.5版本以后出现的新特性。用于解决安全问题,是一个类型安全机制。
好处:
1. 将运行时出现问题ClassCastException,转移到了编译时期。方便于程序员解决问题。让运行时期问题减少,安全。
2. 避免了强制转换。
泛型的格式:通过<>来定义要操作的引用数据类型。
在使用java提供的对象时,什么时候写泛型呢?
通常在集合框架中很常见,只要见到<>就要定义泛型。
其实<>就是用来接收类型的。当使用集合时,将集合中要存储的数据类型作为参数传递到<>中即可。
泛型类:当类中需要操作的引用数据类型不确定的时候,早期定义Object来完成扩展。现在定义泛型来完成扩展。
泛型方法:因为泛型类定义的泛型,在整个类中有效。如果被方法使用,那么泛型类的对象明确要操作的具体类型后,所有要操作的类型就已经固定了。为了让不同方法可 以操作不同类型,而且类型还不确定。那么可以将泛型定义在方法上。(泛型放在返回值类型前面,修饰符后面。)
泛型定义在接口上:
?’通配符,也可以理解为占位符。
泛型限定:用于泛型扩展用的。
?extends E:可以接受E类型或者E的子类型,上限。
?super E:可以接受E类型或者E的父类型,下限。