前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java集合框架详述之(Collection,List,Set)

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

作者头像
百思不得小赵
发布2022-12-01 12:02:23
5910
发布2022-12-01 12:02:23
举报
文章被收录于专栏:小赵Java总结

前言

Collection接口的层次结构图:

一、集合概述

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

  • 集合实际上就是一个容器。可以来容纳其它类型的数据,可以一次容纳多个对象。(数组其实就是一个集合。)
  • 集合不能直接存储基本数据类型,也不能直接存储java对象,集合当中存储的都是java对象的内存地址。(或者说集合中存储的是引用。)
  • 在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):

代码语言:javascript
复制
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));
    }
}

运行结果:

代码语言:javascript
复制
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() ;返回迭代的下一个元素

示例代码(2):

代码语言:javascript
复制
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);
        }
    }
}

运行结果:

代码语言:javascript
复制
100
abc
def
java.lang.Object@723279cf
  • 迭代器重要规律:

1.集合结果发生改变,迭代器需要重新获取 2.迭代过程中不能调用remove()方法删除元素,会出现异常(ConcurrentModificationException) 3.迭代元素的过程中使用迭代器的remove()方法删除元素

示例代码(3):

代码语言:javascript
复制
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());
    }
}

运行结果:

代码语言:javascript
复制
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):

代码语言:javascript
复制
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));
    }
}

运行结果:

代码语言:javascript
复制
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):

代码语言:javascript
复制
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);
        }
    }
}

运行结果:

代码语言:javascript
复制
cccc
bvncm
mnkj

LinkedList类

  • LinkedList底层是基于链表数据结构。
  • LinkedList集合优缺点:

优点:添加和删除数据比较快,随机增删效率高 缺点:查询数据比较慢,检索效率低。

Vector类

  • Vector初始化容量是10.
  • 扩容为原容量的2倍。
  • Vector底层是数组。
  • Vector底层是线程安全的,但是效率低

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

  • 只在编译时起作用,给编译器参考
  • 泛型优缺点:

优点: 1.集合中存储元素类型统一 2.集合中去除掉元素是泛型指定的类型,无需进行向下转型。 缺点:导致集合中元素缺乏多样性

  • JDK8新特性:钻石表达式
代码语言:javascript
复制
List<String> list = new ArrayList<>();
	类型自动推断!

示例代码(6):

使用泛型之前

自定义类:

代码语言:javascript
复制
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("狗在飞");
    }
}

测试类:

代码语言:javascript
复制
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();
            }
        }
    }
}

运行结果:

代码语言:javascript
复制
动物在移动!!
动物在移动!!

使用泛型之后

测试类:

代码语言:javascript
复制
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();
            }
        }
    }
}

运行结果:

代码语言:javascript
复制
动物在移动!!
猫抓老鼠
动物在移动!!
狗在飞

一起找不同吧!!!!!

四、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之前

自定义类:

代码语言:javascript
复制
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;
    }
}

测试类:

代码语言:javascript
复制
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
      }
}

运行结果:

代码语言:javascript
复制
false
s1hashCode值:284720968
s2hashCode值:122883338
2

重写hashCode和equals之后

自定义类:

代码语言:javascript
复制
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);
    }
}

测试类:

代码语言:javascript
复制
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
    }
}

运行结果:

代码语言:javascript
复制
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采用中序遍历方式。

示例代码(8):

代码语言:javascript
复制
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);
        }
    }
}

运行结果:

代码语言:javascript
复制
langlang
make
wangwu
zhangsan
=============================
14
200
300
600

Process finished with exit code 0

从以上结果可以看出,String类和Integer类都实现了这个接口。 示例代码(9):

实现Comparable接口

Customer类:

代码语言:javascript
复制
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 + '}';
    }
}

测试类:

代码语言:javascript
复制
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);
        }
    }
}

运行结果:

代码语言:javascript
复制
Customer{age=100}
Customer{age=65}
Customer{age=46}
Customer{age=30}
Customer{age=23}

示例代码(10):

比较器进行排序

WuGui类:

代码语言:javascript
复制
class WuGui {
    int age;

    public WuGui(int age) {
        this.age = age;
    }

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

比较器:

代码语言:javascript
复制
//单独在这里编写一个比较器
//比较实现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;
    }
}

测试类:

代码语言:javascript
复制
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);
        }
    }
}

运行结果:

代码语言:javascript
复制
WuGui{age=10}
WuGui{age=30}
WuGui{age=50}
WuGui{age=100}
WuGui{age=200}

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

代码语言:javascript
复制
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);
        }
    }
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-07-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
    • Collection接口的层次结构图:
    • 一、集合概述
    • 二、Collection详述
      • Collection接口中的常用方法:
        • 集合的迭代(遍历)
        • 三、List详述
          • List接口的常用方法:
            • ArrayList类
              • LinkedList类
                • Vector类
                  • 泛型机制(jdk1.5之后新特性)
                    • 使用泛型之前
                    • 使用泛型之后
                • 四、Set详述
                  • HashSet类
                    • 哈希表
                    • 没有重写hashCode和equals之前
                    • 重写hashCode和equals之后
                  • TreeSet类
                    • 红黑树(自平衡二叉树)
                    • 实现Comparable接口
                    • 比较器进行排序
                相关产品与服务
                对象存储
                对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档