Javaの集合

集合


1. 集合概述
  • Java 集合就像一种容器,可以把多个对象的引用放入容器中。
  • Java 集合类可以用于存储数量不等的多个对象,还可用于保存具有映射关系的关联数组
  • Java 集合可分为 Set、List 和 Map 三种体系
  • Set:无序、不可重复的集合
  • List:有序,可重复的集合
  • Map:具有映射关系的集合
  • 在 Java5 之前,Java 集合会丢失容器中所有对象的数据类型,把所有对象都当成 Object 类型处理;从 Java5 增加了泛型以后,Java 集合可以记住容器中对象的数据类型。
2. Collection接口
  • Collection 接口是 List、Set 和 Queue 接口的父接口,该接口里定义的方法既可用于操作 Set 集合,也可用于操作 List 和 Queue 集合:

3. List接口
  1. List接口简介
  • List接口为Collection直接接口。List所代表的是有序的Collection,即它用某种特定的插入顺序来维护元素顺序。用户可以对列表中每个元素的插入位置进行精确地控制,

  • 同时可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。实现List接口的集合主要有:ArrayList、LinkedList、Vector、Stack。

    1. ArrayList集合
  • ArrayList内部是通过数组实现的,它允许对元素进行快速随机访问。数组的缺点是每个元素之间不能有间隔,当数组大小不满足时需要增加存储能力,就要讲已经有数组的数据复制到新的存储空间中。

  • 当从ArrayList的中间位置插入或者删除元素时,需要对数组进行复制、移动、代价比较高(ArrayList在内存不够时默认是扩展50% + 1个)。因此,它适合随机查找和遍历,不适合插入和删除。

public class Test3 {
    public static void main(String[] args) {
        List<String > list=new ArrayList<>();
        list.add("456");//追加到末尾
        list.add("hello");
        list.add("12311");
        list.add("12322");
        //1.for
        for(int i=0;i<list.size();i++){
            System.out.println(list.get(i));
        }
        //2.foreach
        for(String s:list){
            System.out.println(s);
        }
        //3.迭代器
        System.out.println("=============");
        Iterator<String> it = list.iterator();
        /*while (true){
            boolean b = it.hasNext();
            if(!b){
                break;
            }
            System.out.println(it.next());
        }*/
        while (it.hasNext()){
            System.out.println(it.next());
        }
    }
}
3. LinkedList集合
  • LinkedList是用双向链表结构存储数据的,很适合数据的动态插入和删除,随机访问和遍历速度比较慢。另外,他还提供了List接口中没有定义的方法,专门用于操作表头和表尾元素,可以当作堆栈、队列和双向队列使用。
4. Vector集合
  • Vector集合底层数据结构是数组实现的,其操作和ArrayList一致,查询快,增删慢,但是其内部是线程安全,效率低。
5. Itrator接口
  • Iterator 接口主要用于遍历 Collection 集合中的元素,Iterator 对象也被称为迭代器

  • Iterator 接口隐藏了各种 Collection 实现类的底层细节,向应用程序提供了遍历 Collection 集合元素的统一编程接口

  • Iterator 仅用于遍历集合,Iterator 本身并不提供承装对象的能力。如果需要创建 Iterator 对象,则必须有一个被迭代的集合。

6. ArrayList和LinkedList的区别如下:
  • ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。

  • 对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。

  • 对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。

自己写一个集合类

public class MyArrayList<T> {
    T[] arr;
    int count = 0;
    int  size = 5;

    public MyArrayList() {
        arr = (T[])new Object[size];
    }

    public void add(T t){
        if(count+1 == size){
            size = size + size/2;

            T[] newarr = (T[])new Object[size];
            System.arraycopy(arr,0,newarr,0,count);
            newarr[count] = t;
            count++;
            arr = newarr;
        }
        else{
            arr[count] = t;
            count++;
        }
    }
    public void add(int index,T t) {
        if (count + 1 == size) {
            size = size + size / 2;
        }
        T[] newarr = (T[]) new Object[size];
        System.arraycopy(arr,0,newarr,0,index);
        newarr[index] = t;
        System.arraycopy(arr,index,newarr,index+1,count - index);
        arr = newarr;
        count++;
    }
    public void clear(){
        arr = null;
    }
    public T[] clone(){
        T[] newArr = arr;
        return newArr;
    }
    public boolean contains(T t){
        boolean isContains = false;
        for (int i = 0; i < count; i++) {
            if(t.equals(arr[i])){
                isContains = true;
            }
            else {
                isContains = false;
            }
        }
        return isContains;
    }
    public void ensureCapacity(int minCapacity){
        size = minCapacity;
    }
    public T get(int index){
        return arr[index];
    }
    public int size(){
        return count;
    }
    public int indexOf(T t){
        for (int i = 0; i < count; i++) {
            if(t.equals(arr[i])){
                return i;
            }
        }
        return -1;
    }
    public boolean isEmpty(){
        if(count==0){
            return true;
        }
        else{
            return false;
        }
    }
    public T[] remove(int index){
        if(index<0){
            System.exit(0);
        }
        else{

            T[] newarr = (T[])new Object[count];

            System.arraycopy(arr,0,newarr,0,index);
            System.arraycopy(arr,index+1,newarr,index,count-index);
//            System.arraycopy(arr,index,newarr,index+1,count - index);
            count--;
            arr = newarr;
        }
        return arr;
    }
    public T[] remove(T t){
        for (int i = 0; i < count; i++) {
            if(t.equals(arr[i])){
                return this.remove(i);
            }
        }
        System.out.println("未找到元素,删除失败!");
        return arr;
    }
    public void show(){
        String s = "[";
        if(count+1 == size){
            size = size + size / 2;
            T[] newarr = (T[])new Object[size];
            System.arraycopy(arr,0,newarr,0,count);
            arr = newarr;
        }
        for (int i = 0; i < count; i++) {
            if(arr[i+1]!=null) {
                s = s + arr[i] + ",";
            }else{
                s = s + arr[i];
            }

        }
        s = s + "]";
        System.out.println(s);

    }
}

Set接口

1).Set接口介绍

Set是Collection子接口;

Set和Collection基本上一样,一点除外:Set无法记住添加的顺序,不允许包含重复的元素。

当试图添加两个相同元素进Set集合,添加操作失败,add()方法返回false。

Setr如何判断两个对象是否相等?HashSet 集合的add()方法底层依赖于双列集合HashMap, 它依赖于两个方法 equals()和hashCode(); 先比较元素equals(), 再比较hashCoede值.

也就是说两个对象equals比较返回true,Set集合是不会接受这个两个对象的。

常用子类:

HashSet:散列存放

TreeSet:有序存放

2).HashSet集合

HashSet 是 Set 接口的典型实现,大多数时候使用 Set 集合时都使用这个实现类。

HashSet 按 Hash 算法来存储集合中的元素,因此具有很好的存取和查找性能。

HashSet 具有以下特点:

不能保证元素的排列顺序

HashSet 不是线程安全的

集合元素可以是 null

当向 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode() 方法来得到该对象的 hashCode 值,然后根据 hashCode 值决定该对象在 HashSet 中的存储位置。如果两个元素的 equals() 方法返回 true,但它们的 hashCode() 返回值不相等,hashSet 将会把它们存储在不同的位置,但依然可以添加成功。

3).TreeSet集合

TreeSet 是 SortedSet 接口的实现类,TreeSet 可以确保集合元素处于排序状态。

TreeSet 支持两种排序方法:自然排序和定制排序。默认情况下,TreeSet 采用自然排序。

自然排序

TreeSet 会调用集合元素的 compareTo(Object obj) 方法来比较元素之间的大小关系,然后将集合元素按升序排列

如果试图把一个对象添加到 TreeSet 时,则该对象的类必须实现 Comparable 接口。

实现 Comparable 的类必须实现 compareTo(Object obj) 方法,两个对象即通过 compareTo(Object obj) 方法的返回值来比较大小。

Comparable 的典型实现:

BigDecimal、BigInteger 以及所有的数值型对应的包装类:按它们对应的数值大小进行比较

Character:按字符的 UNICODE 值来进行比较

Boolean:true 对应的包装类实例大于 false 对应的包装类实例

String:按字符串中字符的 UNICODE 值进行比较

Date、Time:后边的时间、日期比前面的时间、日期大

自然排序步骤:

1.让元素自身具备比较性,

2.实现Compareable接口,覆盖其CompareTo方法

public int compareTo(Object obj)
    {
       if(!(obj instanceof Student)) {
           throw new RuntimeException("不是学生对象");}
       Student s = (Student)obj;
       System.out.println(this.name+"....compareto....."+s.name);
       if(this.age>s.age)
           return 1;
       if(this.age==s.age)
       {
           return this.name.compareTo(s.name);
       }
       return -1;
    }

定制排序

如果需要实现定制排序,则需要在创建 TreeSet 集合对象时,提供一个 Comparator 接口的实现类对象。由该 Comparator 对象负责集合元素的排序逻辑

定制排序步骤:

1)创建比较器,实现comparator接口

2)复写compare方法

3)在创建TreeSet集合对象时,提供一个一个Comparator对象

class MyComparator implements Comparator{//第一步:实现Comparator接口
public int compare(Object o1, Object o2) {//第二步:实现一个campare方法
 if(o1 instanceof Student1 & o2instanceof Student1){
           Student1 s1 =(Student1)o1;
           Student1 s2 =(Student1)o2;
           if(s1.getAge() > s2.getAge()){
              return -1;
           }else if(s1.getAge() < s2.getAge()){
              return 1;
           } }
       return 0;
}}
 
Set<Student1> s= new TreeSet(new  MyComparator());//第三步:创建TreeSet集合对象时,提供一个一个Comparator对象

集合详解