和Java集合谈谈(一)

来源:互联网 发布:淘宝日系女生店铺推荐 编辑:程序博客网 时间:2024/06/06 01:32
                            Collection(IListISetI)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哈希值为:6d06d69cronaldo......24ronaldo......24

结论:自定义对象的hashCode值是不一样的,系统就认为它们是不重复,也就不会调用equals去继续比较。
所以为了在equals中定义我们自己的去除规则,hashCode的返回值要保持一致。

于是就有:

    @Override    public int hashCode() {        //尽量保证hashCode的一致性        return name.hashCode()+age*39;    }
@Overridepublic 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比较器它可以不改变对象的本身来达到排序的效果。

1 0