和Java集合谈谈(一)

                            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
哈希值为: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比较器它可以不改变对象的本身来达到排序的效果。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值