Java之Collection 子接口:List和Set

版权声明:欢迎转载,转载请注明出处哦! https://blog.csdn.net/qq_41647999/article/details/88806372

目录直通车

1、 List接口

① ArrayList

② LinkedList

③ Vector

2、 Set接口

1) HashSet

2) LinkedHashSet

3) TreeSet

两种排序方式


1、 List接口

1) Java中数组用来存储数据的局限性 List集合类中元素有序、且可重复,集合中的每个元素都有其对应的顺序索引。

2)  List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素。

3)  JDK APIList接口的实现类常用的有:ArrayListLinkedListVector

下面是List相对于Collection的新增方法:                     

void add(int index, Object ele)

在下标为index的位置插入值ele
boolean addAll(int index, Collection eles) 在index这个位置,插入eles这个集合
Object get(int index) 获取下标为index的值
int indexOf(Object obj) 返回obj在集合中首次出现的位置,没有则返回-1
int lastIndexOf(Object obj) 返回obj在集合中最后一次出现的位置,没有则返回-1
Object remove(int index) 删除下标为index的值
Object set(int index, Object ele) 设置下标为index的值为ele
List subList(int fromIndex, int toIndex) 返回从formIndex开始(但不包含formIndex)到toIndex(包含toIndex)结束的集合

① ArrayList

根据翻译就是数组集合。是主要的实现类。

如果要在一个一万条数据的数组集合中的首位插入或者是删除一条数据,那么后面的数据都必须依次相应的挪位置,也就是说后面的9999条数据都要变动,代价极大。

② LinkedList

根据翻译就是链表集合。在需要频繁的插入和删除的时候使用。

这个链表就像是C++里面的双向链表(要想知道到底是双向链表还是单向链表,直接检查LinkedList在JDK中的源码就可以了),在双向链表中插入和删除数据是很简单就能实现的。举个上面的那个例子吧,当一条一万条数据的链表中,我要在首位插入一条数据,我只需要new一个空间,然后让这条链表的指针去指向这个new出来的空间就可以了。删除的话只需要先指向下一片空间的地址,然后直接回收这片空间即可,不需要挪位置。

③ Vector

来自JDK1.0的版本,比较古老了。Vector是古老的实现类,线程安全的。不推荐使用了。有兴趣的朋友可以去了解一下。

2、 Set接口

注意:

Set 的特性有无序性、不可重复性。无序性不等于随机性,位置是按照哈希值来定的。

要求添加进Set中的元素所在的类,一定要重写equals()和 hashCode()方法,进而保持Set的不可重复性

那么Set中的元素是如何存储的呢?

普通保持不可重复性的方式是比较吧,比较看这两个数是否一样,那如果在Set中已经有了十万个数,新插入的这个数就需要和后面的十万个数都做一次比较吧,这样的话效率太低了,显然不行。下面揭晓答案:

在Set中是使用的哈希算法来高效率比较以保持不可重复性的。还是假设十万个数值吧,那么这个十万个数值就对应着有十万个不相同的哈希值,也就是说哈希值就相当于是座位。若当前这个位置已有对象存储,再通过equals()比较这两个对象是否相同,相同则后一个对象(第二个相同的值)不能再添加进来。

再注意:

hashCode()要与equals()方法要一致。

重写hashCode方法时,要注意两个位置的值计算出来哈希值相同的问题,比如说30+11=41,但是32+9也等于41。

1) HashSet

是Set的主要实现类。


import java.util.Date;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

public class TestHashSet {

    public static void main(String[] args) {
        Set set = new HashSet();
        /*
        下面的set代码都会有相应的警告(Intellij Idea)或者是感叹号(Eclipse),
        那是因为没有使用泛型,泛型我在
        之后会更新文章的.
         */

        set.add(123);
        set.add(456);
        set.add("AA");
        set.add("AA");
        set.add(new String("AA"));// 两个AA,只有第一个被添加进Set
        set.add(new Date());
        set.add(null); // 这个是set的特别之处
        Person p1 = new Person("Djun",21);
        Person p2 = new Person("Djun",21);
        set.add(p1);
        set.add(p2);
        System.out.println("p1的哈希值:"+ p1.hashCode());
        System.out.println("p2的哈希值:"+ p2.hashCode());
        System.out.println(set.size());
        System.out.println(set);
    }
}

class Student{
    private String name;
    private int age;
    /*
     通过快捷键 getter和setter,toString,equals和hashCode.


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

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Student)) return false;
        Student student = (Student) o;
        return getAge() == student.getAge() &&
                Objects.equals(getName(), student.getName());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getName(), getAge());
    }
}

2) LinkedHashSet

使用双向链表维护一个添加进集合中的顺序。导致当我们便利LinkedHashSet集合元素时,是按照添加进去的顺序。

LinkedHashSet类支持四个构造函数:

LinkedHashSet( );

LinkedHashSet(Collection c);

LinkedHashSet(int capacity) // 构造函数用给定的容量初始化LinkedHashSet

LinkedHashSet(int capacity, float fillRatio); //  传入的容量和填充比初始化LinkedHashSet

import java.util.Date;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

public class TestHashSet {

    public static void main(String[] args) {
        Set set = new HashSet();

        /*
        下面的set代码都会有相应的警告(Intellij Idea)或者是感叹号(Eclipse),
        那是因为没有使用泛型,泛型我在
        之后会更新文章的.
         */

        set.add(123);
        set.add(456);
        set.add("AA");
        set.add("AA");
        set.add(new String("AA"));// 两个AA,只有第一个被添加进Set
        set.add(new Date());
        set.add(null); // 这个是set的特别之处
        Person p1 = new Person("Djun",21);
        Person p2 = new Person("Djun",21);
        set.add(p1);
        set.add(p2);
        System.out.println("p1的哈希值:"+ p1.hashCode());
        System.out.println("p2的哈希值:"+ p2.hashCode());
        System.out.println(set.size());
        System.out.println(set);
    }
}

class Student{
    private String name;
    private int age;
    /*
     通过快捷键 getter和setter,toString,equals和hashCode.


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

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Student)) return false;
        Student student = (Student) o;
        return getAge() == student.getAge() &&
                Objects.equals(getName(), student.getName());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getName(), getAge());
    }
}

3) TreeSet

TreeSet是用来排序的, 可以指定一个顺序, 对象存入之后会按照指定的顺序排列。

import java.util.Iterator;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;

public class TestTreeSet {
    /**
     * TreeSet:
     * 可以按照添加进集合中的元素的制定的顺序遍历,
     * 像String,包装类等默认从小到大的顺序遍历.
     */
    public static void main(String[] args) {
        /*
        以下两种写法都可以,A表示的是类
         TreeSet<A> set = new TreeSet<>();
        */
        Set set = new TreeSet();
        /*
        如果A类里面没有实现Comparable接口时,
        此时向TreeSet中添加Person对象时,会报ClassCastException的错
         */
        /*
        可以按照添加进集合中的元素的制定的顺序遍历,
        像String,包装类等默认从小到大的顺序遍历.
         */
        //        set.add("AA");
        //        set.add("AA");
        //        set.add("JJ");
        //        set.add("GG");
        //        set.add("MM");

        set.add(new A("CC",21));
        set.add(new A("MM",20));
        set.add(new A("GG",19));
        set.add(new A("JJ",23));
        set.add(new A("DD",19));
        System.out.println("TreeSet中所有的元素:" + set);
        Iterator iterator = set.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
}

class A implements Comparable{
    private String name;
    private int age;

    /*
       1、下面这个方法实现了你想按照怎样的规则来排列功能。
       2、 比如这里的情况,当向TreeSet中添加Person类的对象时,
           依据此方法,确定按照哪个属性排列。
        */
     /*通过快捷键初始插入的implement Methods
        @Override
        public int compareTo(Object o) {
            return 0;
        }
    */
     /*
     修改按照指定属性来排列的implement Methods
      */
    @Override
    // 自然排序
    public int compareTo(Object o) {
        if (o instanceof A){
            A p = (A) o;
            // 按照age从小到大来排列 int类型直接减即可
//             return this.age - p.getAge();           


            // 按照namec从小到大的来排列
            return this.name.compareTo(p.getName());
            // 如果想要从大到小来排列只需要加个负号如下:
            // return -this.name.compareTo(p.getName());
        }
        // 当o不是A这个类型的时候就会退出不抛异常
        return 0;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "A{" +
                "name='" + name + '\'' +
                ", age='" + age + '\'' +
                '}';
    }

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof A)) return false;
        A a = (A) o;
        return Objects.equals(name, a.name) &&
                Objects.equals(age, a.age);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

两种排序方式

    a.自然排序(Comparable)

   要求自定义Comparable接口并重写其抽象方法CompareTo()的方法。

         调用对象的compareTo()方法和集合中的对象比较(当前存入的是谁,谁就会调用compareTo方法)

          根据compareTo()方法返回的结果进行存储。

实例如上例!看完例子之后带着问题再往下阅读:

为什么按照age来排序的时候,相同的age只显示了第一个元素?出现这个问题的原因和解决方案是什么?

原因: 向TreeSet中添加元素时,首先按照compareTo()进行比较,一旦返回0,虽然仅是两个对象的此属性值相同,但是程序会认为这两个值时相同的,进而后一个对象就不能添加进来。

解决方案:大家肯定也想到了,排序的时候我让 compareTo()  判断两次,一次判断age是否相同,如果相同再进行name的判断,这两个属性都相同才该元素重复了。

将下面的代码覆盖上例中的compareTo的方法即可,注意观察实现的方法:

    // 自然排序
    public int compareTo(Object o) {
        if (o instanceof A){
            A p = (A) o;
            int i = this.age - p.getAge();
            if (i == 0){
                return this.name.compareTo(p.getName());
            }else{
                return i;
            }
        }
        return 0;
    }

   b.定制排序(Comparator)

1、创建一个实现了Comparator接口的类对象。

2、将此对象作为形参传递给TreeSet的构造器中。

3、 向TreeSet中添加Comparator接口中的compare方法涉及的类的对象属性。

匿名定制排序:


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

public class TestTreeSet2 {
    public static void main(String[] args) {
 

        TreeSet set = new TreeSet(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                if (o1 instanceof Customer && o2 instanceof Customer) {
                    Customer c1 = (Customer) o1;
                    Customer c2 = (Customer) o2;
                    // 判断两个元素是否完全相同
                    int i = c1.getId().compareTo(c2.getId());
                    if (i == 0) {
                        return c1.getName().compareTo(c2.getName());
                    } else {
                        return i;
                    }
                }
                return 0;
            }
        });

        set.add(new Customer("AA",1001));
        set.add(new Customer("AA",1002));
        set.add(new Customer("BB",1003));
        set.add(new Customer("GG",1004));
        set.add(new Customer("DD",1004));
        set.add(new Customer("EE",1000));
        for (Object o : set){
            System.out.println(o);
        }
    }
}
class Customer {
    private String name;
    private Integer id;

    @Override
    public String toString() {
        return "Customer{" +
                "name='" + name + '\'' +
                ", id=" + id +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Customer customer = (Customer) o;
        return Objects.equals(name, customer.name) &&
                Objects.equals(id, customer.id);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, id);
    }

    public Customer(String name, Integer id) {
        this.name = name;
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }
}

   非匿名的定制排序:


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

public class TestTreeSet2 {
    public static void main(String[] args) {
        // 带com名字的Comparator
        Comparator com = new Comparator() {
            /*
            向TreeSet中添加Customer类的对象,
            在compare方法中指明是按照Customer的
            哪一个属性来排序的.
             */
            @Override
            public int compare(Object o1, Object o2) {
                if (o1 instanceof Customer && o2 instanceof Customer){
                    Customer c1 = (Customer) o1;
                    Customer c2 = (Customer) o2;
                    // 判断两个元素是否完全相同
                    int i = c1.getId().compareTo(c2.getId());
                    if ( i == 0){
                        return c1.getName().compareTo(c2.getName());
                    }else{
                        return i;
                    }
                }
                return 0;
            }
        };
        // 将此对象作为形参传递给TreeSet的构造器中
        TreeSet set = new TreeSet(com);
        set.add(new Customer("AA",1001));
        set.add(new Customer("AA",1002));
        set.add(new Customer("BB",1003));
        set.add(new Customer("GG",1004));
        set.add(new Customer("DD",1004));
        set.add(new Customer("EE",1000));
        for (Object o : set){
            System.out.println(o);
        }
    }
}
class Customer {
    private String name;
    private Integer id;

    @Override
    public String toString() {
        return "Customer{" +
                "name='" + name + '\'' +
                ", id=" + id +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Customer customer = (Customer) o;
        return Objects.equals(name, customer.name) &&
                Objects.equals(id, customer.id);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, id);
    }

    public Customer(String name, Integer id) {
        this.name = name;
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }
}

c.两种方式的区别

           TreeSet构造函数什么都不传, 默认按照类中Comparable的顺序(没有就报错ClassCastException)

           TreeSet如果传入Comparator, 就优先按照Comparator

猜你喜欢

转载自blog.csdn.net/qq_41647999/article/details/88806372