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);
        }
    }
}
相关文章
|
2月前
|
算法 Java 数据处理
从HashSet到TreeSet,Java集合框架中的Set接口及其实现类以其“不重复性”要求,彻底改变了处理唯一性数据的方式。
从HashSet到TreeSet,Java集合框架中的Set接口及其实现类以其“不重复性”要求,彻底改变了处理唯一性数据的方式。HashSet基于哈希表实现,提供高效的元素操作;TreeSet则通过红黑树实现元素的自然排序,适合需要有序访问的场景。本文通过示例代码详细介绍了两者的特性和应用场景。
42 6
|
2月前
|
存储 Java 数据处理
Java Set接口凭借其独特的“不重复”特性,在集合框架中占据重要地位
【10月更文挑战第16天】Java Set接口凭借其独特的“不重复”特性,在集合框架中占据重要地位。本文通过快速去重和高效查找两个案例,展示了Set如何简化数据处理流程,提升代码效率。使用HashSet可轻松实现数据去重,而contains方法则提供了快速查找的功能,彰显了Set在处理大量数据时的优势。
33 2
|
2月前
|
存储 算法 Java
Java Set因其“无重复”特性在集合框架中独树一帜
【10月更文挑战第14天】Java Set因其“无重复”特性在集合框架中独树一帜。本文深入解析Set接口及其主要实现类(如HashSet、TreeSet)如何通过特定的数据结构(哈希表、红黑树)确保元素唯一性,并提供最佳实践建议,包括选择合适的Set实现类和正确实现自定义对象的`hashCode()`与`equals()`方法。
31 3
|
2月前
|
安全 Java 程序员
深入Java集合框架:解密List的Fail-Fast与Fail-Safe机制
本文介绍了 Java 中 List 的遍历和删除操作,重点讨论了快速失败(fail-fast)和安全失败(fail-safe)机制。通过普通 for 循环、迭代器和 foreach 循环的对比,详细解释了各种方法的优缺点及适用场景,特别是在多线程环境下的表现。最后推荐了适合高并发场景的 fail-safe 容器,如 CopyOnWriteArrayList 和 ConcurrentHashMap。
60 5
|
2月前
|
存储 Java 数据处理
Set 是 Java 集合框架中的一个接口,不包含重复元素且不保证元素顺序。
【10月更文挑战第16天】Java Set:无序之美,不重复之魅!Set 是 Java 集合框架中的一个接口,不包含重复元素且不保证元素顺序。通过 hashCode() 和 equals() 方法实现唯一性,适用于需要唯一性约束的数据处理。示例代码展示了如何使用 HashSet 添加和遍历元素,体现了 Set 的高效性和简洁性。
35 4
|
2月前
|
存储 Java 数据处理
Set 是 Java 集合框架中的一个接口,不包含重复元素且不保证元素顺序。
Java Set:无序之美,不重复之魅!Set 是 Java 集合框架中的一个接口,不包含重复元素且不保证元素顺序。它通过 hashCode() 和 equals() 方法确保元素唯一性,适用于需要唯一性约束的数据处理。示例代码展示了如何使用 HashSet 实现这一特性。
27 5
|
2月前
|
存储 Java 数据处理
在Java集合框架中,Set接口以其独特的“不重复”特性脱颖而出
【10月更文挑战第14天】在Java集合框架中,Set接口以其独特的“不重复”特性脱颖而出。本文通过两个案例展示了Set的实用性和高效性:快速去重和高效查找。通过将列表转换为HashSet,可以轻松实现去重;而Set的contains方法则提供了快速的元素查找功能。这些特性使Set成为处理大量数据时的利器。
19 4
|
2月前
|
算法 Java 数据处理
从HashSet到TreeSet,Java集合框架中的Set接口及其实现类以其独特的“不重复性”要求,彻底改变了处理唯一性约束数据的方式。
【10月更文挑战第14天】从HashSet到TreeSet,Java集合框架中的Set接口及其实现类以其独特的“不重复性”要求,彻底改变了处理唯一性约束数据的方式。本文深入探讨Set的核心理念,并通过示例代码展示了HashSet和TreeSet的特点和应用场景。
19 2
|
2月前
|
存储 Java 数据处理
Java中的Set接口以其独特的“不重复”特性,在集合框架中占据重要地位。
【10月更文挑战第13天】Java中的Set接口以其独特的“不重复”特性,在集合框架中占据重要地位。本文通过两个案例展示了Set的实用性和高效性:快速去重和高效查找。通过将列表转换为HashSet,可以轻松实现去重;而Set的contains方法则提供了高效的元素查找功能。这些特性使Set在处理大量数据时表现出色,值得我们在日常编程中充分利用。
33 3
|
2月前
|
存储 分布式计算 NoSQL
大数据-40 Redis 类型集合 string list set sorted hash 指令列表 执行结果 附截图
大数据-40 Redis 类型集合 string list set sorted hash 指令列表 执行结果 附截图
27 3