Collection中的List和Set

集合的分类及特点

Collection 1.List:是有序的(此处顺序是指按加入的元素先后顺序),可重复的,可以通过下标对集合进行遍历,修改;

                        -----1.ArrayList

                                                ArrayList的底层是通过数组来实现的,所以相较于LinkedList,ArrayList查找的效率比较高(没有那么多指针,数组要插入的                                                  话,一要扩容,二要挪动数组中的元素,费时费力);

                        -----2.LinkedList

                                                LinkedList的底层是通过链表来实现的,所以相较于ArrayList, LinkedList插入的效率比较高(只要改变指针就行);

                                                学过数据结构后纠正一下,LinkedList底层是双向链表,准确的说是双向对列,相较于单向,查找速度快了一倍(两头开始查找)!

                        -----3.Vector

                                                底层和ArrayList相同,但是Vector加了线程锁,解决了线程安全问题,但是同样的降低了效率;

                 2.Set:无序的,不可重复的;

                        ------1.HashSet

                                    不可重复性:

HashSet<String> hashSet = new HashSet<>();        
        hashSet.add("abc");
        hashSet.add("abc");
        hashSet.add("def");
        for (String str:hashSet){
            System.out.println(str);
        }

 

结果为abc和def,因为重复了就只取一个;

在这儿补充一点,List和HashSet都可一存放null,但HashSet只能存放一个,TreeSet存放null不会报错,但一遍历就会报空指针异常

然后这儿有个坑:

        HashSet<Person> hashSet = new HashSet<>();
        hashSet.add(new Person(18,"小王"));
        hashSet.add(new Person(18,"小王"));
        hashSet.add(new Person(28,"小许"));
        for (Person person:hashSet){
            System.out.println(person);
        }

结果输出三个对象,因为两个小王虽然内容相同,但是是两个不同的对象(地址是不同滴),而且输出是无序的,但每次输出顺序相同.,同一段代码重复运行好几次会发现每次顺序都相同,这说明Set集合输出遵循某种排序方法, 就是hashcode的值.

关于HashSet中HashCode的补充

Set集合的输出顺序是按HashCode排序的,但并不是直接按照HashCode的大小进行排序的.

                        ------2.TreeSet

                                    TreeSet和HashSet的不同之处在于TreeSet对元素进行了排序(但还是不能使用index进行操作)

Set<String> set = new TreeSet<>();
        set.add("f");
        set.add("d");
        set.add("a");
        set.add("b");
        for (String s:set){
            System.out.println(s);
        }

结果为abdf,进行了排序.

但遗憾的是TreeSet只能对int,String等类型进行比较排序,如果放入自定义的类进行输出就会报错.因为对象不能比较大小.

集合的遍历,修改,删除等操作

List集合有序而Set集合无序,意味着List可以操作下标而Set不行!所以在对集合进行遍历或者修改删除的时候应该特别注意.

对List集合遍历,删除有两种方法,即使用迭代器和使用下标

public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<Integer>();
        list.add(5);
        list.add(3);
        list.add(5);
        list.add(6);
        Iterator<Integer> iterator = list.iterator();
        while (iterator.hasNext()){
            if (iterator.next()==3){
                iterator.remove()
                //list.remove(3);
            }
        }
        System.out.println(list);
    }

首先要注意的时迭代器也是有类型的,如果没有填类型,那么默认为Obj类型,这样一来

if (iterator.next()==(Integer)3){  }

需要强转,因为iterator.next的值时obj而不是Integer;//此处强烈注意,掉过两次坑,丢人!

所以迭代器请加泛型谢谢 !

所以迭代器请加泛型谢谢 !

所以迭代器请加泛型谢谢 !

迭代器不操作下标,所以迭代器的方法都不带下标.

在迭代过程中请勿直接操作集合元素,不如被注释掉的list.remove,他打乱了迭代器,会让迭代器报错.

另外迭代器比较坑爹的两个方法,一个叫next,一个叫remove

iterator.next:返回迭代器的下一个元素

iterator.remove:很坑爹,作用时删除返回的上一个元素而不是下一个元素,跟next放一起我怀疑是☀公司专门恶心人的!

由此带来的一个隐患就是如果你一上来就使用iterator.remove就会报错:java.lang.IllegalStateException

当然这不是最恶心的,实际操作发现

public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<Integer>();
        list.add(5);
        list.add(3);
        list.add(5);
        list.add(6);

        Iterator<Integer> iterator = list.iterator();
        iterator.next();
        iterator.remove();
        iterator.remove();
        System.out.println(list);
    }

上诉代码照样报java.lang.IllegalStateException错误,原因是next返回的元素被第一个remove删除了,第二个remove找不到上一个返回的元素,疯狂报错

使用iterator.remove()之前必须使用iterator.next();

使用iterator.remove()之前必须使用iterator.next(); 

使用iterator.remove()之前必须使用iterator.next(); 

第二种对集合操作的方法是使用循环,此处需注意的是:虽然加强for循环也是循环,但for each不能操作下标,其根本原因是for each其实底层也是迭代器!!!!!

对于Set类型集合,因为是无序的无法操作下标,就只能用迭代器来对其操作,当然包括for each.

经实验发现:

public class LinkedDemo {
    public static void main(String[] args) {
        LinkedList<Integer> list = new LinkedList<>();
        list.add(2);
        list.add(2);
        list.add(1);
        list.add(2);
        list.addFirst(666);
        list.addLast(888);
        list.remove((Integer) 1);
        System.out.println(list);
    }
}

当集合为integer类型,要删除时按元素而不是按下标时:

使用list.remove((Integer)1);

强转后删除

拓展:比较器

什么是比较器:接口comparable和接口compartor的实现类即为比较器.

1.1内部比较器,实现comparable接口.我通过Car类去实现comparable接口(comparable接口中只有compareTo一个方法)成功的实现了对ArrayList<Person>集合的排序.

public class Person implements Comparable{
    private String name;
    private int age;
    private double money;
    public Person(String name, int age, double money) {
        this.name = name;
        this.age = age;
        this.money = money;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", money=" + money +
                '}';
    }
    @Override
    public int compareTo(Object o) {
        Person p = (Person)o;
//      return age-p.age;   返回两个对象age的差值
        return   money>p.money?1:money==p.money?0:-1; //大于形参money返回1,等于形参返回0,小于返回-1
//      return name.compareTo(p.name);//String类型用compareTo比较

    }
}

实验发现,返回属性差值,或者直接指定返回1,0,-1都行.

之后

 public class Collection {

public static void main(String[] args) {
List<Person> pList = new ArrayList<Person>();
pList.add(new Person("aa",88,899));
pList.add(new Person("bb",9,10000));
pList.add( new Person("cc",100,666));
System.out.println(pList);
}
}

输出结果为

[Person{name='cc', age=100, money=666.0}, Person{name='aa', age=88, money=899.0}, Person{name='bb', age=9, money=10000.0}]

结果为按照Person类里面内部比较器中按money属性升序排列.Person类里面比较器比什么就按什么排序.

public int compareTo(Object o) {
        Person p = (Person)o;
        int i = age - p.getAge();
        int j = name.compareTo(p.getName());
        return i==0?j:i;
    }

内部比较器还可以比较两个值来进行排序,即如上代码:先比较age,如果age相同,再按name排序

内部比较器在List和Set接口都可以用内部比较器来排序,

外部比较器需要配合Collections.sort()方法使用,但这个方法不能放Set集合

1.2外部比较器.

首先是一个Person类

public class Person {
    String name;
    int age;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

然后写外部比较器

public class PersonCompartor implements Comparator {
    @Override
    public int compare(Object o1, Object o2) {
        Person p1 = (Person)o1;
        Person p2 = (Person)o2;
        /*通过age比较,p1比p2大就返回1,相等就返回0,小于就返回-1*/
        int result = p1.age>p2.age?1:(p1.age==p2.age)?0:-1;
        return result ;
    }
}

这边使用了List集合,可直接通过Collections.sort方法进行排序(小补充:Collection是集合框架的父接口,Collections是一个包装类,里面有很多关于集合的常用方法)

        List<Person>list = new ArrayList<>();
        list.add(new Person("小齐",25));
        list.add(new Person("小李",20));
        list.add(new Person("小王",15));
        list.add(new Person("小周",48));
        Collections.sort(list,new PersonCompartor());
        System.out.println(list);

输出结果即为:

[Person{name='小王', age=15}, Person{name='小李', age=20}, Person{name='小齐', age=25}, Person{name='小周', age=48}]

想改变升序或者降序只需改变外部比较器即可,当然可以写两个不同的比较器升序降序随你选;

1.3无论是实现Comparator还是Comparable接口,都应该使用返回-1,0,1的方式来实现接口而不是两个值相减(在为数值类型的情况下)

因为如果两个小数相减,可能会得到一个很小的数值,但是比较器的返回值是int类型,故需要向上取整然后强转成int类型,太麻烦了,故直接返回-1,0,1比较简单.

匿名内部类应用在比较器中的应用

public static void main(String[] args) {
        List<Person> list = new ArrayList<>();
        list.add(new Person("小齐",25));
        list.add(new Person("小李",20));
        list.add(new Person("小王",15));
        list.add(new Person("小周",48));
        Collections.sort(list, new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                Person p1 = (Person)o1;
                Person p2 = (Person)o2;
                /*按年龄顺序降序排列*/
                return p1.getAge()>p2.getAge()?-1:p1.getAge()==p2.getAge()?0:1;
            }
        });
        System.out.println(list);
    }

相较于之前先建立Compator的子类,然后在Collection.sort()中new子类的对象,直接在此处建立内部类更加的简便了.

Set类型的集合使用外部比较器进行排序(集合之间的互相转化!!!)

public static void main(String[] args) {
        LinkedHashSet<Integer> lhs = new LinkedHashSet<>();
        lhs.add(1);
        lhs.add(5);
        lhs.add(19);
        lhs.add(8);
        lhs.add(10);
        System.out.println(lhs);
        ArrayList<Integer> List = new ArrayList<>(lhs);
        Collections.sort(List);
    }

可以通过集合的有参构造方法方法转换集合的类型

比如ArrayList<Integer> List = new ArrayList<>(lhs);

这一行代码即将lhs(LinkedHashSet)集合转变成了ArrayList集合

然后就可以用Collections.sort进行排序了

同理,也可以将List的实现类集合转变成Set集合.

额外补充LinkedHashSet

LinkedHashSet的底层是链表,他与HashSet的不同之处在于,LinkedHashSet是有序的(这儿的有序并不是与TreeSet一样经过排序之后输出,而是与List集合一样,遵循输出顺序按照add顺序)

public static void main(String[] args) {
        LinkedHashSet<Integer> lhs = new LinkedHashSet<>();
        lhs.add(1);
        lhs.add(5);
        lhs.add(19);
        lhs.add(8);
        lhs.add(10);
        System.out.println(lhs);
    }

输出的顺序为add顺序

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值