Java集合框架详述之(Collection,List,Set)

简介: Collection 是 List 和 Set 的父接口

前言


Collection接口的层次结构图:

1.png



一、集合概述


所有的集合类和集合接口都在java.util包下。


集合实际上就是一个容器。可以来容纳其它类型的数据,可以一次容纳多个对象。(数组其实就是一个集合。)


集合不能直接存储基本数据类型,也不能直接存储java对象,集合当中存储的都是java对象的内存地址。(或者说集合中存储的是引用。)


2.png


在java中每一个不同的集合,底层会对应不同的数据结构。往不同的集合中存储元素,等于将数据放到了不同的数据结构当中。(例如:数组、二叉树、链表、哈希表…以上这些都是常见的数据结构。)


在java中集合分为两大类:


一类是单个方式存储元素:

单个方式存储元素,这一类集合中超级父接口:java.util.Collection;

一类是以键值对儿的方式存储元素

以键值对的方式存储元素,这一类集合中超级父接口:java.util.Map;


二、Collection详述


Collection 是 List 和 Set 的父接口


Collection接口中的常用方法:


boolean add(E e) 向集合中添加元素

int size();获取集合中的元素个数

void clear();清空集合

boolean contains(Object o) 判断当前集合是否含有o元素

boolean remove(Object o) 删除集合中的元素

boolean isEmpty() 判断集合是否为空

Object[] toArray() 把集合转换为数组


示例代码(1):


import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
public class CollectionText {
    public static void main(String[] args) {
        Collection collection = new ArrayList();
        //向集合中添加元素
        boolean b1 = collection.add(100);
        boolean b2 = collection.add("你好,java");
        boolean b3 = collection.add("中国");
        System.out.println(b1);
        System.out.println(b2);
        System.out.println(b3);
        //获取集合中的元素个数
        int i1 = collection.size();
        System.out.println(i1);
        //collection.clear();
        //System.out.println(collection.size());
        boolean b = collection.contains(100);
        System.out.println(b);
          //删除集合中的元素
        collection.remove("中国");
        System.out.println(collection.size());
         //判断集合是否为空
        System.out.println(collection.isEmpty());
        collection.clear();
        System.out.println(collection.isEmpty());
        collection.add("我");
        collection.add("是");
        collection.add("中");
        collection.add("国");
        collection.add("人");
        //转换为数组
        Object[] objects=collection.toArray();
        for(int i=0;i<objects.length;i++){
            Object o=objects[i];
            System.out.println(o);
        }
        System.out.println(Arrays.toString(objects));
    }
}

运行结果:


true
true
true
3
true
2
false
true
[我, 是, 中, 国, 人]
Process finished with exit code 0

注:Collection在使用泛型之前,可存放Object的所有子类,在使用泛型之后,只能存某个具体的类型。


集合的迭代(遍历)


迭代器是一个对象


所有Collection以及子类通用,Map集合不能使用。


使用迭代器的步骤:

第一步:获取集合对象的迭代器对象

第二部:通过获取的迭代器对象进行迭代(遍历)


迭代器(iterator)中的方法:

boolean hasNext(); true:有元素迭代 false:无元素迭代

E next() ;返回迭代的下一个元素


1.png


示例代码(2):


import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollectionText01 {
    public static void main(String[] args) {
        //创建集合
        Collection c1=new ArrayList();
        c1.add(100);
        c1.add("abc");
        c1.add("def");
        c1.add(new Object());
        //第一步:获取集合对象的迭代器对象iterator
        Iterator it=c1.iterator();
        //第二步通过获取的迭代器对象进行集合遍历/迭代。
        while (it.hasNext()){
            Object obj=it.next();
            System.out.println(obj);
        }
    }
}

运行结果:


100
abc
def
java.lang.Object@723279cf


迭代器重要规律:

1.集合结果发生改变,迭代器需要重新获取

2.迭代过程中不能调用remove()方法删除元素,会出现异常(ConcurrentModificationException)

3.迭代元素的过程中使用迭代器的remove()方法删除元素


示例代码(3):


import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollectionText05 {
    public static void main(String[] args) {
        Collection c=new ArrayList();
        //ConcurrentModificationException异常!!!
        //Iterator it=c.iterator();
        c.add(1);
        c.add(2);
        c.add(3);
        Iterator it=c.iterator();
        while (it.hasNext()){
            Object o=it.next();
            // 没有更新迭代器
            //c.remove();直接通过集合,没有通知迭代器(导致迭代器快照与原集合状态不同)
            //更新迭代器 。
            it.remove(); //删除的是迭代器指向的当前对象。
            System.out.println(o);
        }
        System.out.println(c.size());
    }
}

运行结果:


1
2
3
0


三、List详述


List集合存储元素特点:有序,可重复!

他们都是有顺序的,也就是放进去是什么顺序,取出来还是什么顺序,也就是基于线性存储,可以看作是一个可变数组


List 接口下面主要有三个实现 ArrayList 、LinkedList和Vector


List接口的常用方法:


void add(int index, E element) 在列表中指定的位置上插入指定的元素

E get(int index) 根据下标获取元素

int indexOf(Object o) 返回此列表中指定元素的第一个出现的索引

int lastIndexOf(Object o) 返回此列表中指定元素的最后一个发生的索引

E remove(int index) 移除此列表中指定下标的元素

E set(int index, E element) 用指定元素替换此列表中指定位置的元素


示例代码(4):


import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class ListText01 {
    public static void main(String[] args) {
        //创建list类型集合
        List list=new ArrayList();
        //添加元素,将指定的元素到这个列表的末尾
        list.add("a");
        list.add("b");
        list.add("c");
        //在列表中指定的位置上插入指定的元素(效率低!!!)
        list.add(1,"ooo");
        //迭代器
        Iterator it=list.iterator();
        while(it.hasNext()){
            Object o=it.next();
            System.out.println(o);
        }
        System.out.println("=================================");
        //根据下标获取元素
        Object o=list.get(0);
        System.out.println(o);
        //list集合特有的遍历方法!!
        for(int i=0;i<list.size();i++){
            System.out.println( list.get(i));
        }
        // 返回此列表中指定元素的第一个出现的索引
        System.out.println(list.indexOf("b"));
        //返回此列表中指定元素的最后一个发生的索引
        System.out.println(list.lastIndexOf("c"));
        System.out.println("=============================");
        //移除此列表中指定位置的元素
        list.remove(1);
        System.out.println(list.size());//3
        //用指定元素替换此列表中指定位置的元素
        list.set(0,"n");
        System.out.println(list.get(0));
    }
}

运行结果:


a
ooo
b
c
==============================
a
a
ooo
b
c
2
3
=============================
3
n


ArrayList类


ArrayList底层是Object类型的数组。

ArrayList集合初始化容量10。添加第一个元素时,创建长度为10的空数组。

扩容机制:扩容为原容量1.5倍。


ArrayList集合优化:尽可能少的扩容,数组扩容效率低。


ArrayList集合优缺点:

优点:查询数据比较快,检索效率高

缺点:添加和删除数据比较慢,无法存大数据量(向数组末尾添加元素效率高)


ArrayList是非线程安全


示例代码(5):


import java.util.*;
public class ArrayListText01 {
    public static void main(String[] args) {
       //初始化容量为10
        List list1=new ArrayList();
        //初始化容量为20
        List  list2=new ArrayList(20);
        Collection c=new ArrayList();
        c.add("cccc");
        c.add("bvncm");
        c.add("mnkj");
        Iterator it=c.iterator();   
        while (it.hasNext()){
            Object o= it.next();
            System.out.println(o);
        }
    }
}

运行结果:


cccc
bvncm
mnkj


LinkedList类


LinkedList底层是基于链表数据结构。


LinkedList集合优缺点:

优点:添加和删除数据比较快,随机增删效率高

缺点:查询数据比较慢,检索效率低。


Vector类


Vector初始化容量是10.

扩容为原容量的2倍。

Vector底层是数组。

Vector底层是线程安全的,但是效率低


泛型机制(jdk1.5之后新特性)


只在编译时起作用,给编译器参考


泛型优缺点:

优点:

1.集合中存储元素类型统一

2.集合中去除掉元素是泛型指定的类型,无需进行向下转型。

缺点:导致集合中元素缺乏多样性


JDK8新特性:钻石表达式

List<String> list = new ArrayList<>();
  类型自动推断!


示例代码(6):


使用泛型之前


自定义类:


class Animal{
    public void run(){
        System.out.println("动物在移动!!");
    }
}
class Cat extends Animal{
    public void catchMouse(){
        System.out.println("猫抓老鼠");
    }
}
class Dog extends Animal{
    public void fly(){
        System.out.println("狗在飞");
    }
}

测试类:


import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
 * jdk5.0之后的特性:泛型
 */
public class GenericText {
    //使用泛型之前
    public static void main(String[] args) {
        List list = new ArrayList();
        Cat cat = new Cat();
        Dog dog = new Dog();
        list.add(cat);
        list.add(dog);
        Iterator it = list.iterator();
        while (it.hasNext()) {
            Object o = it.next();
            if (o instanceof Animal) {
                ((Animal) o).run();
            }
        }
    }
}

运行结果:


动物在移动!!
动物在移动!!


使用泛型之后


测试类:


public class GenericText {
    public static void main(String[] args) {
        //使用泛型List<Animal>后,表示List集合只能存储Animal类型的元素
        //泛型指定集合中存储的数据类型
        List<Animal> list = new ArrayList<Animal>();
        Cat cat = new Cat();
        Dog dog = new Dog();
        list.add(cat);
        list.add(dog);
        //表示迭代器迭代Animal类型
        Iterator<Animal> it = list.iterator();
        while (it.hasNext()) {
            //使用泛型后,迭代器每一次返回的数据是Animal类型
            Animal a = it.next();
            //无需强转,直接调用
            a.run();
            if (a instanceof Cat) {
                ((Cat) a).catchMouse();
            } else if (a instanceof Dog) {
                ((Dog) a).fly();
            }
        }
    }
}

运行结果:


动物在移动!!
猫抓老鼠
动物在移动!!
狗在飞


一起找不同吧!!!!!


四、Set详述


Set集合存储元素特点:无序不可重复。

无序表示存进去是这个顺序,取出来就不一定是这个顺序了,另外Set集合中元素没有下标。Set集合中的元素还不能重复。


HashSet类


HashSet 中的数据是无序的不可重复的。HashSet 按照哈希算法存取数据的,具有非常好性能,它的工作原理是这样的,当向 HashSet 中插入数据的时候,他会调用对象的 hashCode 得到该对象的哈希码,然后根据哈希码计算出该对象插入到集合中的位置。


HashSet不是同步的;

集合元素值可以是null;

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


HashSet集合判断两个元素的标准是两个对象通过equals方法比较相等,并且两个对象的hashCode方法返回值也相等。


特别是向 HashSet 或 HashMap 中加入数据时必须同时覆盖 equals 和 hashCode 方法,应该养成一种习惯覆盖 equals 的同时最好同时覆盖 hashCode


Java语法要求:

两个对象 equals 相等,那么它的 hashcode 相等

两个对象 equals 不相等,那么它的 hashcode 并不要求它不相等,但一般建议不相等

hashcode 相等不代表两个对象相等(采用 equals 比较)


哈希表


一个元素为链表的数组,综合了数组与链表的优点。

示例代码(7):


没有重写hashCode和equals之前


自定义类:


public class Student {
    private String name;
    public Student() {
    }
    public Student(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}


测试类:


import java.util.HashSet;
import java.util.Set;
public class HashMapText02 {
    public static void main(String[] args) {
        Student s1 = new Student("zhangsan");
        Student s2 = new Student("zhangsan");
        System.out.println(s1.equals(s2));
        //重写hashCode之前
        System.out.println("s1hashCode值:" + s1.hashCode());
        System.out.println("s2hashCode值:" + s2.hashCode());
        Set<Student> set=new HashSet<>();
        set.add(s1);
        set.add(s2);
        System.out.println(set.size());//2
      }
}

运行结果:


false
s1hashCode值:284720968
s2hashCode值:122883338
2


重写hashCode和equals之后


自定义类:


public class Student {
    private String name;
    public Student() {
    }
    public Student(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    //equals方法
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return Objects.equals(name, student.name);
    }
    //hashCode方法
    public int hashCode() {
        return Objects.hash(name);
    }
}


测试类:


import java.util.HashSet;
import java.util.Set;
public class HashMapText02 {
    public static void main(String[] args) {
        Student s1 = new Student("zhangsan");
        Student s2 = new Student("zhangsan");
        System.out.println(s1.equals(s2));
        //重写hashCode之后
        System.out.println("s1hashCode值:" + s1.hashCode());
        System.out.println("s2hashCode值:" + s2.hashCode());
   Set<Student> set=new HashSet<>();
        set.add(s1);
        set.add(s2);
        System.out.println(set.size());//1
    }
}

运行结果:


true
s1hashCode值:-1432604525
s2hashCode值:-1432604525
1


找呀找呀,找不同。


TreeSet类


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

TreeSet 可以对 Set 集合进行排序,默认自然排序(即升序)

TreeSet 是可排序的

TreeSet集合对自定义数据可以排序方法:


第一种:Java提供了一个Comparable接口,该接口里定义了一个compareTo(Object obj)方法,该方法返回一个整数值,实现该接口的类必须实现该方法,实现了该接口的类必须实现该方法,实现接口的类就可以比较大小了。当调用一个一个对象调用该方法与另一个对象进行比较obj1.compareTo(obj2)如果返回0表示两个对象相等;如果返回正整数则表明obj1大于obj2,如果是负整数则相反。

第二种:在构造TreeSet或TreeMap集合时给他一个比较器对象。比较规则自己写!!!


Comparable 与Comparator区别?

当比较规则不会发生改变时或比较规则只用一个时建议实现Comparable接口

当比较规则有多个,并且需要多个比较规则之间进行切换,建议使用与Comparator

编写比较器可以改变规则!!!

Comparator区别符合OCT原则


红黑树(自平衡二叉树)


TreeSet内部实现的是红黑树,默认整形排序为从小到大。

三种遍历方式:前序遍历,中序遍历,后序遍历。(前中后指的是根的位置)

TreeSet采用中序遍历方式。


1.png


示例代码(8):


import java.util.TreeSet;
public class TreeMapText01 {
    public static void main(String[] args) {
        //
        TreeSet<String> ts=new TreeSet();
        ts.add("zhangsan");
        ts.add("wangwu");
        ts.add("make");
        ts.add("langlang");
        for (String s: ts){
            System.out.println(s);
        }
        System.out.println("=============================");
        TreeSet<Integer> ts2=new TreeSet();
        ts2.add(200);
        ts2.add(300);
        ts2.add(600);
        ts2.add(14);
        for (Integer i:ts2){
            System.out.println(i);
        }
    }
}


运行结果:


langlang
make
wangwu
zhangsan
=============================
14
200
300
600
Process finished with exit code 0


从以上结果可以看出,String类和Integer类都实现了这个接口。


示例代码(9):


实现Comparable接口


Customer类:


class Customer implements Comparable<Customer>{
    int age;
    public Customer(int age) {
        this.age = age;
    }
    //需要在此方法写比较的逻辑,或者说出比较规则,按照什么进行比较!!
    //比较规则自己定
    public int compareTo(Customer o) {//c1.comperTo(c2);
        //this是c1,o是c2
        //c1与c2比较,就是this与c比较
       // return this.age-o.age;
        return o.age-this.age;
    }
    @Override
    public String toString() {
        return "Customer{" + "age=" + age + '}';
    }
}

测试类:


import java.util.TreeSet;
public class TreeSetText03 {
    public static void main(String[] args) {
        Customer p = new Customer(23);
        Customer p2 = new Customer(100);
        Customer p3 = new Customer(30);
        Customer p4 = new Customer(65);
        Customer p5 = new Customer(46);
        TreeSet<Customer> treeSet = new TreeSet<>();
        treeSet.add(p);
        treeSet.add(p2);
        treeSet.add(p3);
        treeSet.add(p4);
        treeSet.add(p5);
        for (Customer c : treeSet) {
            System.out.println(c);
        }
    }
}


运行结果:


Customer{age=100}
Customer{age=65}
Customer{age=46}
Customer{age=30}
Customer{age=23}


示例代码(10):


比较器进行排序


WuGui类:


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


比较器:


//单独在这里编写一个比较器
//比较实现java.util.Comparator接口。(Comparable是java.lang包下的,Comparator接口是java.util包下)
class WuguiComparator implements Comparator<WuGui> {
    public int compare(WuGui o1, WuGui o2) {
        return o1.age - o2.age;
    }
}

测试类:


import java.util.Comparator;
import java.util.TreeSet;
public class TreeSetText05 {
    public static void main(String[] args) {
        //创建TreeSet时,需要使用比较器
        //TreeSet<WuGui> wuGuis=new TreeSet<>();这样不行,没有通过构造方法构造一个比较器进去
        //给构造方法添加一个比较器
        TreeSet<WuGui> wuGuis = new TreeSet<>(new WuguiComparator());
        wuGuis.add(new WuGui(200));
        wuGuis.add(new WuGui(30));
        wuGuis.add(new WuGui(50));
        wuGuis.add(new WuGui(10));
        wuGuis.add(new WuGui(100));
        for (WuGui w : wuGuis) {
            System.out.println(w);
        }
    }
}

运行结果:


WuGui{age=10}
WuGui{age=30}
WuGui{age=50}
WuGui{age=100}
WuGui{age=200}


改进上面测试类(使用匿名内部类)代码如下:


public class TreeSetText05 {
    public static void main(String[] args) {
  //第三种!! //使用匿名内部类的方式  这个类没有名字,直接new接口!!
        TreeSet<WuGui> wuGuis = new TreeSet<>(new Comparator<WuGui>() {
            @Override
            public int compare(WuGui o1, WuGui o2) {
                return o1.age - o2.age;
            }
        });
        wuGuis.add(new WuGui(200));
        wuGuis.add(new WuGui(30));
        wuGuis.add(new WuGui(50));
        wuGuis.add(new WuGui(10));
        wuGuis.add(new WuGui(100));
        for (WuGui w : wuGuis) {
            System.out.println(w);
        }
    }
}
相关文章
|
1天前
|
存储 Java C++
Java集合篇之深度解析Queue,单端队列、双端队列、优先级队列、阻塞队列
Java集合篇之深度解析Queue,单端队列、双端队列、优先级队列、阻塞队列
8 0
|
13天前
|
存储 Java 编译器
Java集合丛林:深入了解集合框架的秘密
Java集合丛林:深入了解集合框架的秘密
15 0
Java集合丛林:深入了解集合框架的秘密
|
16天前
|
Java BI
Java 获取周,月,年日期集合(统计图)
Java 获取周,月,年日期集合(统计图)
Java 获取周,月,年日期集合(统计图)
|
17天前
使用Vant框架的组件van-pull-refresh搭配van-list和van-card完成上滑加载更多列表数据,下拉刷新当前列表数据(等同于翻页功能)
使用Vant框架的组件van-pull-refresh搭配van-list和van-card完成上滑加载更多列表数据,下拉刷新当前列表数据(等同于翻页功能)
|
23天前
|
Java
Java使用List去重的四中方式
Java使用List去重的四中方式
17 6
|
1月前
|
存储 安全 Java
java集合框架及其特点(List、Set、Queue、Map)
java集合框架及其特点(List、Set、Queue、Map)
|
1月前
|
Java
JAVA——List中剔除空元素(null)的三种方法汇总
JAVA——List中剔除空元素(null)的三种方法汇总
|
1月前
|
安全 Java API
Java并发 - J.U.C并发容器类 list、set、queue
Queue API 阻塞是通过 condition 来实现的,可参考 Java 并发 - Lock 接口 ArrayBlockingQueue 阻塞 LinkedBlockingQueue 阻塞 ArrayQueue 非阻塞 LinkedQueue 非阻塞
|
1月前
|
存储 安全 Java
【Java】集合(一)单列集合List
【Java】集合(一)单列集合List
22 0
|
1月前
|
Java API
java 对象list 使用stream进行过滤
在Java中,你可以使用Stream API对对象列表进行过滤。假设你有一个`List<MyObject>`,并且你想根据某些条件过滤出特定的对象。以下是一个示例: ```java import java.util.List; import java.util.stream.Collectors; public class Main { public static void main(String[] args) { List<MyObject> myObjects = ... // 初始化你的对象列表 List<MyObject> filter