Collection(I)
List(I) Set(I)
ArrayList LinkedList Vector HashSet TreeSet
一,List
上面的每一种容器的数据结构都是不同的,而Collection接口就是它们不断抽取出的共性的内容。
List下的集合,它们的共同点就是,集合中的元素都是有索引。
也因为有索引,所以就有了和Collection下稍稍不同的地方。
List另有个特点,有序,可以存储重复元素。
1.1 和Collection的不同之处
①比如就有因为有角标index而有的一些特有的方法(相较于Collection):
//添加集合元素到指定的位置
void add(int index,E element)
//添加集合到指定的位置
void addAll(int index, Collection<? extends E> c)
//根据角标获取集合元素
get(int index)
//根据角标删除集合元素
remove(int index)
//根据角标替换集合元素
set(int index, E element)
//根据角标返回子集合
List<E> subList(int fromIndex,int toIndex)
②迭代器加强
Collection下的iterator方法,它返回的是Iterator接口。
Iterator迭代器为什么会是一个接口呢?
上面说过,每一种容器的数据结构是不同的,而”存”和”取”的动作却是相同的。但是”存”和”取”这些动作(可能还有其他的动作)并不可以用一个方法或多个方法就能搞掂。
所以需要使用一个类来表示,”存”和”取”虽然都有,但实际”存”和”取”的每一种容器的处理过程却不同,于是一层一层的抽出了一个接口Iterator。
该接口有三个方法,hasNext(),next(),remove()。
ArrayList的iterator():
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
//实现了Iterator接口
}
Itr是ArrayList的内部类,这样做的好处就是可以很方便的访问类中的成员。
我们不必关心Itr中怎么去重写那三个方法。因为已经封装好了,拿过来用就ok。
而List下的获取迭代器除了iterator方法,还有一个加强的listIterator方法。返回的是ListIterator列表迭代器。不同于迭代器而言,它可以自由的遍历,Iteraotr只可以正序,而列表迭代器还可以逆序。不仅如此,还可以在遍历时,修改和添加集合中的元素。这些只有加强的迭代器可以做到。
ArrayList al=new ArrayList();
al.add("000");
al.add("001");
al.add("002");
ListIterator it=al.listIterator();
while(it.hasNext()){
Object obj=it.next();
if(obj.equals("001")){
it.remove();//删除
it.add("003");//添加(独有)
}
}
1.2 List下的每个容器的特点
ArrayList
数据结构——>数组结构
便于查询,增删比较麻烦。ArrayList查询某个元素,遍历一遍就可以得知。而增删某个元素,都会导致集合中的元素移动。
LinkedList
数据结构——>链表结构
不便于查询,增删很方便。LinkedList中的元素是后面的元素记录前面的元素,如果查询的话,遍历时,就要不断的判断后面的元素是否记录着前一个。较低效。而增删的改变却微乎其微,只需要增删元素的前后元素的引用改变就可以完成增删。
Vector
数据结构——>数组结构
不同于ArrayList的是,它的线程是同步的。效率较低。已经ArrayList被替代。
那么如果多线程操作集合怎么办?由于同步效率很低,所以即使线程不安全我们也要用ArrayList。同时,我们也可以自己加锁解决安全问题。
1.3 ArrayList去除重复元素
去除普通元素的思路,新定义一个新的ArrayList集合,然后通过迭代器迭代需要去除重复元素的集合,在迭代中判断,如果新的集合中不包含迭代出的元素,就把该元素添加到新集合,最后返回新集合。
对于普通类型的元素,比如String,int等。是可以去除的。原因就是在判断时会调用contains方法,而contains底层调用的就是equals。而基本类型的equals方法是复写了Object的equals的。
对于String,它比较的是对象之间的内容是否相同,对于int,比较的是值是否相同。
而如果ArrayList添加的是自定义的对象,而自定义对象的equals毫无疑问是参照Object的equals。它比较的是对象之间的内存地址是不是相同。
而对于自定义的不同对象,它们的内存地址肯定是不同的,所以有必要复写equals,定义我们自己的规则。
class Person{
private String name;
private int age;
Person(String name,int age){
this.name=name;
this.age=age;
}
public int getAge(){
return age;
}
public String getName(){
return name;
}
@Override
public boolean equals(Object obj) {
if(!(obj instanceof Person)){
//不相同的对象,肯定不重复返回false
return false;
}
Person p=(Person)obj;
//下面的情况我们才认为是重复的元素
return this.name.equals(p.getName())&&
this.age==p.getAge();
}
}
二,Set
Set集合,是一种简单的集合。无序,而且不可以存储重复元素。
分为HashSet,TreeSet。前者是哈希表的结构,后者是树结构。
3.1 HashSet去除重复元素
private static void method_0() {
HashSet hs=new HashSet();
hs.add("java01");
hs.add("java02");
hs.add("java02");
System.out.println
("java02".hashCode()=="java02".hashCode());
//返回的HashCode值是相同的
hs.add("java03");
hs.add("java03");
hs.add("java04");
Iterator it=hs.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
打印的结果,去除了重复元素。那么它是怎么做到的呢?
那就不得不提Object的hashCode方法。它返回的是对象的哈希值。这里的哈希值并不是对象的内存地址值。但是有一点可以确定,那就是同一个对象的哈希值一定相同。
而hashCode和equals方法之间还有千丝万缕的联系。
如果对象的hashCode的返回值不一样,JVM就会认为它们是不同的对象,于是会偷懒,也就不会再调用equals去比较。
而如果返回的哈希值相同,一定会调用equals去比较。
对于上面的例子,不同的值它们的hashcode值肯定是不一样的,而相同的值它们的hashCode的值必定一样,所以这时会在add的时候,调用equals。String的equals判断它们之间的对象的值是否相同。当相同时,add就会返回false,表示添加失败。
HashSet就通过这样去除了重复的元素。
而HashSet存储自定义的对象是否可以去除重复呢?
private static void method_1() {
HashSet hs=new HashSet();
hs.add(new Student("ronaldo",24));
hs.add(new Student("ronaldo",24));
Iterator iterator=hs.iterator();
while(iterator.hasNext()){
Student stu=(Student) iterator.next();
Syso(stu.getName()+"......"+stu.getAge());
}
}
class Student{
private String name;
private int age;
Student(String name,int age){
this.name=name;
this.age=age;
}
public int getAge(){
return age;
}
public String getName(){
return name;
}
@Override
public int hashCode() {
Syso(
"hashCode值"+
Integer.toHexString(super.hashCode()));
return super.hashCode();
}
@Override
public boolean equals(Object obj) {
System.out.println("equals执行");
return super.equals(obj);
}
}
执行后的结果:
哈希值为:15db9742
哈希值为:6d06d69c
ronaldo......24
ronaldo......24
结论:自定义对象的hashCode值是不一样的,系统就认为它们是不重复,也就不会调用equals去继续比较。
所以为了在equals中定义我们自己的去除规则,hashCode的返回值要保持一致。
于是就有:
@Override
public int hashCode() {
//尽量保证hashCode的一致性
return name.hashCode()+age*39;
}
@Override
public boolean equals(Object obj) {
if(!(obj instanceof Student)){
return false;
}
Student stu=(Student)obj;
return this.name.equals(stu.getName())
&&this.age==stu.getAge();
}
3.2 TreeSet排序
TreeSet兼容Set集合的特性,由于它树形的结构的特点,我们可以对集合中的元素进行排序。
TreeSet的默认排序规则,是按照首字母在ASCII码表的前后位置来进行排序,也称为自然排序法。
要进行自然排序,前提是需要实现Compareable接口,复写compareTo方法。该接口会强行为每个对象进行自然排序。
对于基本类型,它们都已经实现了Compareable接口。
private static void method_0() {
TreeSet ts=new TreeSet();
ts.add("cba");
ts.add("aaa");
ts.add("bca");
ts.add("Dbcd");
Iterator it=ts.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
而对于自定义的对象,必须要实现Compareable接口,否则在TreeSet进行排序时,会抛出ClassCaetException异常。原因很简单,TreeSet必须要知道根据什么来排序,实现Compareable接口,在compareTo中定义我们排序的规则。
int compareTo(T o)
当前对象和此对象比较,如果大于此对象,返回正数,小于,返回负数。相等,返回0。返回0表示是重复元素,就会将其pass掉。
改写为:
private static void method_1() {
TreeSet ts=new TreeSet();
ts.add(new StudentTree("wcx02",22));
ts.add(new StudentTree("wcx007",20));
ts.add(new StudentTree("wcx09",19));
ts.add(new StudentTree("wcx09",19));
ts.add(new StudentTree("wcx08",19));
Iterator it=ts.iterator();
while(it.hasNext()){
Object obj=it.next();
StudentTree stu=(StudentTree)obj;
Syso(stu.getName()+" "+stu.getAge());
}
}
class StudentTree implements Comparable<StudentTree>{
private String name;
private int age;
StudentTree(String name,int age){
this.name=name;
this.age=age;
}
public int getAge(){
return age;
}
public String getName(){
return name;
}
@Override
public int compareTo(StudentTree o) {
if(this.age>o.getAge()){
return 1;
}
if(this.age==o.getAge()){
return this.name.compareTo(o.getName());
}
if(this.age<o.getAge()){
return -1;
}
return 0;
}
}
这时,有一种情况我需要考虑。String,Integer这些已经实现了自然排序的类,如果我不想自然排序,想换另一种。难道我们要去修改compareTo中的代码么?
还有就是其他人已经写好的类已经实现Compareable,它的排序规需要修改,去更改别人写好的排序规则,这也是不妥的。
public TreeSet(Comparator<? super E> comparator)
TreeSet的构建器中接收一个比较器,在Comparator中的compare中规定我们的规则。
int compare(T o1,T o2)
o1大于,小于,等于o2会分别返回正数,负数,0。
Compareable和Comparator:
虽然它们都可以实现排序,但一个在对象内部实现,一个在对象外部。
Comparator比较器它可以不改变对象的本身来达到排序的效果。