Java学习记录(八)单列集合

Collection

collection是所有单列集合的父类接口,,他的功能是全部单列集合都能继承使用的

使用方法,利用多态进行创建实现类对象

Collection<类型> coll = new ArrayList<>();

这时候coll就能使用Collection里的各种方法

List系列集合

List系列集合的特点:添加元素是有序,可重复,有索引的

有序:相当于队列,先进先出,例如{1,2,3,8}的格式存入,出来的格式也应是{1,2,3,8}

重复:集合中的数据是可以重复的

有索引:每个数据都有索引,能通过索引获取里面的元素

迭代器(一般是需要在集合里删除元素使用)

在访问set系列集合时,因为set系列集合没有索引,那么怎么访问集合里的值呢,这个时候就需要迭代器,可以将迭代器看成一个指针,在创建迭代器的初始时刻,这个指针指向该集合的头一个数据元素,如下

Iterator<类型> iterator = coll.iterator();

而需要遍历里面的元素,则只需要将指针不断的后移,并得到指针所指向的数据就行

iterator.hasNext()此方法是用来判断指针指向的数据是否为空,如果不为空返回true,如果为空,返回false,利用这个特性,能将这个方法用来当循环条件
iterator.next();这个方法是将指针此时指向的数据返回,并将指针向后移动一位

(指针在结束迭代之后,是不会复位的,如果还想迭代,只能重新创造一个迭代器)

具体用法代码如下:

package com.itazhang.demo1;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class Test7{
    public static void main(String[] args) {
        Collection<String> arrayList1 = new ArrayList<>();
        arrayList1.add("aaa");
        arrayList1.add("bbb");
        arrayList1.add("ccc");
        //创建迭代器,相当于指针,指向集合的第一个元素
        Iterator<String> iterator = arrayList1.iterator();

        boolean empty = arrayList1.isEmpty();
        System.out.println(empty);
        //在while循环里的方法是判断当前指针指向的对象是否为空,如果不为空,返回true,如果为空,返回false
        while(iterator.hasNext()){
            //iterator.next()该方法是返回该指针所指向的值
            String next = iterator.next();
            System.out.println(next);
        }
    }
}

在迭代器指针移动时,不能用集合的方法去删除对象,只能用迭代器的删除方法去删除指针现在所指向的数据,具体代码实现如下

package com.itazhang.demo1;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class Test7{
    public static void main(String[] args) {
        Collection<String> arrayList1 = new ArrayList<>();
        arrayList1.add("aaa");
        arrayList1.add("bbb");
        arrayList1.add("ccc");
        //创建迭代器,相当于指针,指向集合的第一个元素
        Iterator<String> iterator = arrayList1.iterator();

        boolean empty = arrayList1.isEmpty();
        System.out.println(empty);
        //在while循环里的方法是判断当前指针指向的对象是否为空,如果不为空,返回true,如果为空,返回false
        while(iterator.hasNext()){
            //iterator.next()该方法是返回该指针所指向的值
            String next = iterator.next();
            if ("bbb".equals(next)){
                //删除该指针指向的数据
                iterator.remove();
            }
        }
        //打印该集合对象,会发现需要删除的数据已经被删除
        System.out.println(arrayList1);
    }
}

增强for

当然也能用增强for循环来遍历集合和数组中的内容

格式为:for(数据类型  变量名 :数组或集合){

                语句;

}

这里的变量名就是可以理解为迭代器指针指向的数据,而需要访问该数据,直接在循环中打印该变量就行。

例如: for(String s : list){

        System.out.println(s);

}

具体实现代码如下:

package com.itazhang.demo1;

import java.util.ArrayList;
import java.util.Collection;

public class Test8 {
    public static void main(String[] args) {
        Collection<String> coll = new ArrayList<>();
        coll.add("aaa");
        coll.add("bbb");
        coll.add("ccc");
        coll.add("ddd");
        for (String s : coll){
            System.out.println(s);
        }
    }
}

也能用collection里的foreach方法遍历

具体实现如下:

package com.itazhang.demo1;

import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Consumer;

public class Test8 {
    public static void main(String[] args) {
        Collection<String> coll = new ArrayList<>();
        coll.add("aaa");
        coll.add("bbb");
        coll.add("ccc");
        coll.add("ddd");
        coll.forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });
    }
}

因为此时foreach里需要传递的是一个接口,所以需要对这个接口进行重写,于是采用匿名内部类的方法此时里面的变量s就是可以理解为指针指向的数据。

set系列集合

set系列集合有无序,不重复,无索引的特点,刚好与List系列相反

主要有

Hashset:主要特点为无序,不重复,无索引

LinkedHashset:主要特点为有序,不重复,无索引

Treeset:主要特点为:可排序,不重复,无索引

Hashset(在要求存储数据时不能存储一样的数据时使用)

Hashset:主要特点为无序,不重复,无索引

在hashset中,他添加元素时底层其实是用地址值去做哈希处理存储,他的底层时一个数据,每个索引里有一串链表或者红黑树,而在存储这些值的时候是将这个数据相关的地址做一定处理再存入到相应的位置,而如果与之前存储的数据相同则会直接舍弃该数据。

而这也可以解释为啥hashset的特点为无序,不重复,无索引。

无序:因为他底层是用数组和链表以及红黑树来存储的,所以在存储数据时不是按序列存储的,而是将值存入数组中的链表或者红黑树,而不同索引下的链表其实是没什么顺序关系的,所以不能具体确定他的顺序。

不重复:在存入数据的时候,如果存入的数据跟之前的一些数据相同,因为算出来的哈希值跟之前存入的数据相同,所以会存入跟之前相同数据一样索引的数组位置下的链表中,而在链表存储的时候,会从链表的头部开始从上往下与该值比较,如果有与该值相同的数据,则该值直接被舍弃

无索引:跟无序一样,因为是存储在数组中的链表下,所以没法用索引来判断数据存储的位置。

因为上诉存储数据的方法,所以在使用hashset存储自定义数据的时候,我不想存储相同的元素,就能利用hashset不能存储相同数据的特点,让他存储时就将相同的数据存入不了,但是注意:

因为是自定义的数据类型,Java中并没有帮我们重写equals方法和hashcode方法,所以在存储的时候,他是默认将地址值带入哈希值运算存储,这个时候我们得重写这两个方法让他们比较的是属性值,而不是我们创建存储对象的地址值,而在存储基础数据类型时,Java帮我们写好了这两个方法,所以不用重写。

下面是一个小案例,要求不能添加重复属性的学生对象:

1、创建Student类,并重写equals方法和hashcode方法,让他添加时用属性值作为哈希值参与计算,从而使不同属性值的对象不能被添加

package com.itazhang.Myset;

import java.util.Objects;

public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }
    //重写的equals方法,比较的是属性值
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age && Objects.equals(name, student.name);
    }
    //重写hashCode方法,返回的是属性值对应的hash表值
    //如果没有重写hashCode方法,返回的是地址值对应的hash表值
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }
}

2、创建Student对象,并实现不能重复添加相同属性对象的方法

package com.itazhang.Myset;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class HashSetDemo1 {
    public static void main(String[] args) {
        Student stu1 = new Student("azhang",22);
        Student stu2 = new Student("azhang",22);
        Student stu3 = new Student("lisi",22);
        Student stu4 = new Student("zhangsan",22);
        Set<Student> s = new HashSet<>();
        s.add(stu1);
        s.add(stu2);
        s.add(stu3);
        s.add(stu4);
        Iterator<Student> it = s.iterator();
        while(it.hasNext()){
            Student temp = it.next();
            System.out.print(temp + " ");
        }

    }
}

运行结果:能看到相同属性值的学生对象并没有被添加到hashset集合中

Linkedhashse(在要求存入数据时不能存储相同的数据且取出的时候按照存入数据的顺序取出时使用)

LinkedHashset:主要特点为有序,不重复,无索引

这个结构是在hashset基础上在从头节点(也就是第一个存储的数据)开始,每两个值之间存在一个双向链表而这两个值会各自存在一个空间来记录对方的地址,从而能根据记录的地址值找到对方,相当于这几个数据会根据存入的顺序形成一个新的双向链表,所以这个结构是有序的,怎么存入的,就会怎么取出,顺序相同,下列为这个结构的示意图

下面为代码实现:要求不能存入同一属性的学生,且在读取时与存入顺序一致,创建类与之前的Student类相同,所以只放实现类代码:

package com.itazhang.Myset;

import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;

public class HashSetDemo1 {
    public static void main(String[] args) {
        Student stu1 = new Student("azhang",22);
        Student stu2 = new Student("azhang",22);
        Student stu3 = new Student("lisi",22);
        Student stu4 = new Student("zhangsan",22);
        Set<Student> s = new LinkedHashSet<>();
        s.add(stu1);
        s.add(stu2);
        s.add(stu3);
        s.add(stu4);
        Iterator<Student> it = s.iterator();
        while(it.hasNext()){
            Student temp = it.next();
            System.out.print(temp + " ");
        }

    }
}

运行截图:这个时候打印出的数据顺序就会跟之前存储的数据顺序相同

Treeset(在存入数据不能重复且输出数据需要自动排序时使用)

Treeset:主要特点为:可排序,不重复,无索引

该结构是在存入数据的时候自动排序的,而且他的底层使用红黑树创建的,但是如果存入的是基本数据类型,他是默认用基本数据类型的比较方法来排序的,例如存入{4,3},那么会输出{3,4}。而如果输入的是{b,a},那么就输出{a,b}因为他会自动将字符转为asscal码来进行比较,而字符串的话就会默认从第一个字符开始比较{"aaa","ddd"," ccc","bbb"}这个时候就会将每个字符串的首字符用来比较,首字符大的在后面,如果首字符相同,则比较第二个字符,依次类推。

第一种排序方式(适合只有一种排序条件时使用)

但如果存储的是自己创建的自定义类,例如我自己创建的一个Student类,这时候得让Student类去实现一个Comparable接口,并重写里面的方法,这个方法就是比较的规则,如下:

package com.itazhang.Myset;

import java.util.Objects;

public class Student implements Comparable<Student>{
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }

    @Override
    public int compareTo(Student o) {
        //按照年龄从小到大排列
        int temp = this.getAge() - o.getAge();
        return temp;
    }
}

这个时候this就是现在要存储的数据,o就是之前已经存储的数据,上诉代码是按年龄大小来比较,

即 int temp = this.getAge() - o.getAge();这个temp如果是正数就是从小到大排列,如果为负数就是从大到小排列,这个与他的底层存储结构也就是红黑树有关系。

实现类代码如下:

package com.itazhang.Myset;

import java.util.TreeSet;

public class TreesetDemo1 {
    public static void main(String[] args) {
        Student stu1 = new Student("azhang",21);
        Student stu2 = new Student("azhang",21);
        Student stu3 = new Student("lisi",24);
        Student stu4 = new Student("zhangsan",22);
        TreeSet<Student> t = new TreeSet<>();
        t.add(stu1);
        t.add(stu2);
        t.add(stu3);
        t.add(stu4);
        System.out.println(t);

    }
}

运行截图如下,可以看到,取出数据的时候,Treeset集合自动按照排序规则也就是我们重写的接口里面的方法,将我们存入的对象排序了(上诉代码为按照年龄排序),运行截图如下:

第二种排序方式,使用比较器比较(适合多种排序条件下使用)

有时候在输出数据时,我们要求是多个限制条件输出,这个时候就可以使用比较器,比较器也是一个接口,直接在Treeset创建时就直接将这个接口里的方法重写,重写的方法就是比较规则。

例如:

TreeSet<String> tr = new TreeSet<>(new Comparator<String>() {
    @Override
    public int compare(String o1, String o2) {
        //先按照字符串长度排序,如果长度相同,按照默认的字符串比较方法排序
        int i = o1.length() - o2.length();
        i = i==0?o1.compareTo(o2):i;
        return i;
    }
});

这个时候,比较器里的方法表示返回值i是表示字符串长度,如果字符串长度相同,则i表示调用字符串比较方法传回的值,总之,这个方法里就是一种比较规则。具体代码如下:

package com.itazhang.Myset;

import java.util.Comparator;
import java.util.TreeSet;

public class TreesetDomo2 {
    public static void main(String[] args) {
        TreeSet<String> tr = new TreeSet<>(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                int i = o1.length() - o2.length();
                i = i==0?o1.compareTo(o2):i;
                return i;
            }
        });
        tr.add("c");
        tr.add("cdd");
        tr.add("acc");
        System.out.println(tr);
    }
}

运行结果如下,与方法里的规则一致,先按字符串长度从小到大进行排列,如果字符串长度相同,则按照默认的字符串比较方法,也就是字符串中的每个字符进行比较

练习:需求如下

代码实现如下:

package com.itazhang.Myset;

import java.util.Comparator;
import java.util.TreeSet;





public class TreesetExercise1 {
    public static void main(String[] args) {
        Student stu1 = new Student("zhangsan",23,90,99,50);
        Student stu2 = new Student("lisi",24,90,98,50);
        Student stu3 = new Student("wangwu",25,95,100,30);
        Student stu4 = new Student("azhang",26,60,99,70);
        Student stu5 = new Student("alei",26,70,80,70);
        TreeSet<Student> tr = new TreeSet<>(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                int temp;
                temp = (o1.getCgrade()+o1.getEgrade()+o1.getMgrade()) - (o2.getCgrade()+o2.getEgrade()+o2.getMgrade());
                temp = temp == 0?temp =o1.getCgrade()-o1.getCgrade():temp;
                temp = temp == 0?temp =o1.getMgrade()-o2.getMgrade():temp;
                temp = temp == 0?temp =o1.getEgrade()-o2.getEgrade():temp;
                temp = temp == 0?temp =o1.getAge()-o2.getAge():temp;
                temp = temp == 0?temp =o1.getName().compareTo(o2.getName()):temp;
                return temp;
            }
        });
        tr.add(stu1);
        tr.add(stu2);
        tr.add(stu3);
        tr.add(stu4);
        tr.add(stu5);
        for (Student student : tr) {
            System.out.println(student);
        }
    }
}

运行结果如下:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值