简单了解集合

简单了解集合,第1张

简单了解集合 1.主要集合概述 1.1 什么是集合?

数组其实就是一个集合。集合实际上就是一个容器。可以来容纳其它类型的数据。

1.2 集合有什么用?

集合为什么说在开发使用较多?

集合是一个容器,是一个载体,可以一次容纳多个对象。在实际开发中,假设连接数据库,数据库当中有10条记录,那么假设把这10条数据查询出来,在java程序中会将10条数据封装成10个java对象(new出来的对象),然后将10个对象放到某一个集合当中,将集合传到前端,然后遍历集合,将一个数据一个数据展现出来。

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

list.add(100);//自动装箱Integer注意:

    集合在java中本身也是一个容器,是一个对象。集合中任何时候存储的都是"引用"。
1.3 集合存储结构图

集合可以存储集合,集合存储的是对象的地址值

1.4 不同的集合对应不同的数据结构

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

你往集合c1中放数据。可能是放到了数组上了。
你往集合c2中放数据,可能是放到了二叉树上了。

new ArrayList(); 创建一个集合对象,底层是数据。
new TreeSet(); 创建一个集合对象,底层是二叉树。

1.5 Collection集合继承结构图

在java中集合分为两大类:
一类是单个方式存储元素:
单个方式存储元素,这一类集合中的超级父接口:java.util.Collection;

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

集合继承结构图

List接口的实现类

ArrayList集合
底层采用了数组这种数据结构。Array集合是非线程安全的。

linkedList集合
底层采用了双向链表的数据结构。

Vector集合
底层采用了数组这种数据结构。Vector集合是线程安全的。Vector所有的方法都是有synchronized关键字 修饰,所以线程安全,但是效率低,现在有别的方案,所以Vector使用较少了。

Set接口的实现类

HashSet集合

​ 实际上HashSet集合在new的时候,底层实际上new了一个HashMap集合。想HashSet集合中存储元素,实际上是存储到HashMap集合中了。HashMap集合是一个哈希表数据结构。

SortedSet接口继承Set接口,实现类是TreeSet

SortedSet集合存储元素的特点:由于继承了Set集合,所以它的特点是无序不可重复(没有下标),但是放在SortedSet集合中元素可以自动排序。我们称为可排序集合,放到该集合中的元素是自动按照大小顺序排序的。

TreeSet集合底层实际上是TreeMap,new TreeSet集合的时候,底层实际上new了一个TreeMap集合。往TreeSet集合中存储数据的时候,实际上是往TreeMap集合中存储数据。TreeMap集合底层采用了二叉树的数据结构。

1.6 Map集合继承结构图

总结(所有的实现类):

ArrayList:底层是数组。linkedList:底层是双向链表。Vector:底层是数组,线程安全的,效率低,使用较少。HashSet:底层是HashMap,放到HashSet集合中的元素等同于放到了HashMap集合key部分了。TreeSet:底层是TreeMap,放到TreeSet集合中的元素等同于放到了TreeMap集合key部分了。HashMap:底层是哈希表。Hashtable:底层是哈希表,线程安全,效率低,使用较少。Properties:是线程安全的,并且key和value只能存户字符串String。TreeMap:底层是二叉树。TreeMap集合的key可以自定安装大小顺序排序。

    List集合存储元素的特点:

    有序可重复的

    有序:存储进去的顺序和取出的顺序是一样的,List集合中的元素有下标,怎么进去就怎么出来。重复:存进去1,可以在存储一个1。

    Set(Map)集合存储元素的特点:

    无序不可重复

    无序:存进去的顺序和取出来的顺序不一定相同,Set集合红的元素没有下标。不可重复:存进去1,就不可在存储1了。

    SortedSet(SortedMap)集合存储元素的特点:

    无序不可重复

    无序:存进去的顺序和取出来的顺序不一定相同,Set集合红的元素没有下标。不可重复:存进去1,就不可在存储1了。

    可排序

    排序:可以按照大小顺序排列。

    Map集合的key,就是以Set集合。往Set集合中方数据,实际上放到了Map集合的key部分。

2.Collection和Iterator 2.1 collection

关于Collection的常用方法

package com.ccy.java.List.Collection;

import java.util.ArrayList;
import java.util.Collection;


public class Collection01 {
    public static void main(String[] args) {
        //创建一个集合对象
        //Collection collection = new Collection();//接口是抽象的,无法实例化
        //多态
        Collection al = new ArrayList();
        //测试Collection接口中的常用方法
        al.add(1200);//自定装箱(java5的新特性)实际上是放进去了一个对象的内存地址。Integer x = new Integer(1200);
        al.add(new Object());
        al.add(true);//自动装箱

        //获取集合红元素的个数
        System.out.println("集合中元素的个数:"+al.size()); //3

        //清空集合中的元素
        System.out.println("清空集合中的元素,结果如下显示:");
        al.clear();
        System.out.println("集合中元素的个数:"+al.size()); //0

        //再向集合中添加元素
        al.add("nihao");//“nihao”对象的内存地址放到了集合中
        al.add("word");
        System.out.println("集合中元素的个数:"+al.size()); //2

        //判断集合中是否包含指定的元素
        boolean word = al.contains("word");
        boolean word2 = al.contains("word2");
        System.out.println(word);//true
        System.out.println(word2);//false

        //删除集合中的某个元素
        System.out.println("删除集合中的某个元素,结果如下:");
        al.remove("word");
        System.out.println("集合中元素的个数:"+al.size()); //1

        //判断集合是否为空
        boolean empty = al.isEmpty();
        System.out.println(empty);//false

        //将集合转换成数组(了解)
        System.out.println("遍历数组,结果如下:");
        al.add(100);
        al.add("abcd");
        Object[] objects = al.toArray();
        for(int i = 0;i 

结果显示

集合中元素的个数:3
清空集合中的元素,结果如下显示:
集合中元素的个数:0
集合中元素的个数:2
true
false
删除集合中的某个元素,结果如下:
集合中元素的个数:1
false
遍历数组,结果如下:
nihao
100
abcd
2.2 Iterator

关于集合的迭代/遍历

package com.ccy.java.List.Collection;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;


public class Collection02 {
    public static void main(String[] args) {
        //以下遍历/迭代的方式,是所有Collection通用的一种方式(包括子类),但在Map集合中不能使用
        //创建一个ArrayList集合对象
        System.out.println("ArrayList集合:");
        Collection c = new ArrayList();
        //向集合中添加元素
        c.add(100);
        c.add("hello");
        c.add("nihao");
        c.add(new Student());
        //第一步:获取集合对象的迭代器对象Iterator
        Iterator it = c.iterator();
        
        //取出集合中的数据,返回的Object对象
        while (it.hasNext()){
            //存进去什么类型,取出来还是什么类型
            Object next = it.next();
            //只是在打印时,prinlt会将其他类型转换成字符串类型打印输出
            System.out.println(next);
        }

        System.out.println();
        System.out.println("HashSet集合:");
        //创建一个HashSet集合对象
        Collection hs = new HashSet();//无序不可重复
        //向HashSet集合中添加元素
        hs.add(10);
        hs.add(30);
        hs.add(1);
        hs.add(100);
        hs.add(10);
        //获取迭代器
        Iterator i = hs.iterator();
        //迭代数据
        while(i.hasNext()){
            System.out.println(i.next());
        }
    }
}

class Student{ }

显示结果

ArrayList集合:
100
hello
nihao
com.ccy.java.List.Collection.Student@4554617c

HashSet集合:
1
100
10
30
2.3 contains原理

package com.ccy.java.List.Collection;

import java.util.ArrayList;
import java.util.Collection;


public class Contains03 {
    public static void main(String[] args) {
        Collection c = new ArrayList();
        String str1 = new String("abc");
        c.add(str1);

        String str2 = new String("ccy");
        c.add(str1);

        String str3 = new String("abc");
        //str3没有添加到集合中
        //c.contains(str3)在底层是这么写的:str3.equals(str1)
        System.out.println(c.contains(str3));//true
    }
}
2.4 contains和remove原理测试

测试contains方法

结论:存放在一个集合中的类型,一定要重写equals方法,如果不是自己new出来的对象就不用重写equals方法

package com.ccy.java.List.Collection;

import java.util.ArrayList;
import java.util.Collection;


public class contains04 {
    public static void main(String[] args) {
        //创建ArrayList集合对象
        Collection c = new ArrayList();
        //实例化User对象
        User ccy1 = new User("ccy");
        //向集合中添加元素
        c.add(ccy1);
        //实例化User对象
        User ccy2 = new User("ccy");
        //自己new出来的对象,没有重写equals方法,则会使用Object中的equals方法,比较的是地址值
        //System.out.println(c.contains(ccy2));//false
        //User类重写了equals方法,比较的是内容值
        System.out.println(c.contains(ccy2));//true
        
        //删除ccy2在集合中的元素
        c.remove(ccy2);//使用remove方法,底层会调用equals方法,删除ccy2就相当于删除了ccy1
        System.out.println(c.size());//0
    }

}
class User{
    private String name;
    public User() { }
    public User(String name) { this.name = name; }
    //重写equals方法
    //如果调用了当前类,也会调用equals方法,但是一定调用的是重写过的方法
    //这个equals方法的比较原理:只要内容一样,则返回true
    @Override
    public boolean equals(Object o){
        //判断集合中为空或者是传进来的o对象不是当前的类。则返回false
        if(o == null || !(o instanceof User)) return false;
        //判断传进来的o对象的地址值和当前类的地址值是否是同一个地址值,是则返回true
        if(o == this) return true;
        //将传进来的o对象强转为当前类
        User u = (User)o;
        //重写过的equals的方法,比较的是内容,而不是比较地址值了
        return u.name == this.name;
    }
}

关于remove方法

当集合的结构发生改变时,迭代器必须重新获取,如果不刷新则会出现异常:java.util.ConcurrentModificationException

在迭代集合元素的过程中,不能调用集合对象的remove方法删除元素(c.remove()),因为使用了会报错:java.util.ConcurrentModificationException

在迭代的过程中,一定要使用迭代器Iterator的remove方法删除元素,不要使用集合自带的remove方法删除元素,会报错:java.util.ConcurrentModificationException

3.List接口 3.1 和list接口特有的方法

void add(int index, E element) 将指定元素插入此列表中的指定位置(可选 *** 作)。

E get(int index) 返回此列表中指定位置的元素,也可是遍历是的一种方式。

int indexOf(Object o) 返回此列表中第一次出现的指定元素的索引,如果此列表不包含该元素,则返回-1。

int lastIndexOf(Object o) 返回此列表中指定元素最后一次出现的索引,如果此列表不包含该元素,则返回-1。

E remove(int index) 删除此列表中指定位置的元素(可选 *** 作)。

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

3.2 ArrayList源码分析

    默认初始化容量10(底层先创建一个长度为0的数组,当添加一个元素的时候,初始化才会变为10)

    集合底层是一个Object[]数组

    构造方法

    new ArrayList(); 默认容量是10new ArrayList(20);new ArrayList(); 参数可以是一个集合

    		//默认容量是10
    		List list1 = new ArrayList();	
    		//容量初始化20
    		List list2 = new ArrayList(20);
    		//构造方法的参数可以是一个集合
    		Collection ha = new HashSet();
    		ha.add("nihao");
            List list3 = new ArrayList(ha); 
            for (int i = 0; i < ha.size(); i++) {
                System.out.println(list.get(i));//nihao
            }
    

    ArrayList集合的扩容

    增长到原容量的1.5倍

    ​ ArrayList集合底层是数组,怎么优化?

    ​ 尽可能减少扩容,因为数组扩容了效率比较低,建议在使用ArrayList集合的时候预估计元素的个数,给 定一个初始化容量

    数组的优点:检索效率比较高(每个元素占位空间大小相同,内存地址是连续的,知道首元素内存地址,然后知道下标,通过数学表达式计算元素的内存地址,所有检索效率高)

    数组的缺点:

    随机增删元素效率低数组无法存储大数据容量,很难找到一块巨大的连续的内存空间

    向数组末尾添加元素,效率效率还是挺高的

3.3 linkedList集合

单向链表结构图

单向链表的基本单位是节点,节点有两个属:数据、下一个节点对象的内存地址

链表的优点:

​ 由于链表上的元素的内存地址是不连续的,所以随机增删的时候不会有大量高的元素位移,因此增删效率较高,以后在开发中,如果遇到增删集合中的元素,就可以使用linkedList集合。

链表的缺点:

​ 不能通过数学表达式计算出被查找元素的内存地址,每一次查找都是从头节点一个一个的遍历,直到找到为止,所有linkedList集合的检索/查找的效率不高。

注意:
ArrayList集合的检索效率高,不是因为他有下标,而是他底层是一个数组
linkedList集合照样有下标,但是他底层是双向链表,只能从头节点一个一个遍历

双向链表结构图

linkedList集合内存结构图(通过源码分析而来)

注意:

//linkedList集合有初始化容量吗?没有
//最初这个链表中没有任何元素,first和last引用都是null
//不管是ArrayList集合还是linkedList集合,以后写代码时不需要关心具体是哪个集合的,因为我们面向的是接口编程,调用的都是接口中的方法
List list = new ArrayList();//这样写表示底层是数组
List link = new linkedList();//这样写表示底层是双向链表
3.4 Vector
    Vector底层是一个数组初始化容量是10扩展之后是原来的2倍 10—>20---->40…Vector是线程安全的,用的比较少java.utils.Collections是一个工具类,可以将非线程安全的转换成线程安全的,Collections.synchronizedList(传非线程的集合对象);
3.5 迭代的几种方式
package com.ccy.java.List.Collection;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;


public class Generic03 {
public static void main(String[] args) {
    List list = new ArrayList<>();
    list.add("nihao");
    list.add("hello");
    list.add("123");

    //迭代器
    Iterator i = list.iterator();
    while (i.hasNext()){
        String next = i.next();
        System.out.println(next);
    }

    //for循环
    for (int j = 0; j < list.size(); j++) {
        String s = list.get(j);//get(index); 是List接口中的特有的方法
        System.out.println(s);
    }

    //foreach
    //foreach的缺点:就是没有下标,全部都遍历出来
    for (String s : list) {
        System.out.println(s);
    }
  }
}
4.Set接口 5.Map接口 6.Collection工具类 7.泛型(Generic) 7.1 初步了解泛型
    JDK5.0之后推出的新特性:泛型泛型这种语法机制,只在编译阶段起作用,只是给编译器参考的。(运行阶段时没用)使用泛型有什么好处?

    集合中存储的元素同一了。(被约束了)从集合中取出的数据类型是泛型指定的类型,不需要大量的向下转型 使用泛型的缺点?

    导致集合中存储的元素没有多样化

package com.ccy.java.List.Collection;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;


public class Generic01 {
    public static void main(String[] args) {
        //创建ArrayList集合对象
        List list = new ArrayList();
        //实例化对象
        Cat cat = new Cat();
        Bird bird = new Bird();
        //向集合中添加数据
        list.add(cat);
        list.add(bird);

        //迭代数据
        //使用了泛型,只能存储指定的泛型类型的数据,存储其他的数据类型的就会报错
        Iterator i = list.iterator();
        while (i.hasNext()){
            Animal next = i.next();
            next.move();
            //判断是否属于Cat类型的,然后进行强转
            if(next instanceof Cat){
                ((Cat) next).run();
            }
            //判断是否属于Bird类型的,然后进行强转
            if(next instanceof Bird){
                ((Bird) next).fly();
            }
        }
    }
}
class Animal{
    public void move(){
        System.out.println("动物在移动!");
    }
}
class Cat extends Animal{
    public void run(){
        System.out.println("猫在运动!");
    }
}
class Bird extends Animal{
    public void fly(){
        System.out.println("鸟儿在飞!");
    }
}

显示结果

动物在移动!
猫在运动!
动物在移动!
鸟儿在飞!
7.2 砖石表达式

JDK之后引入了:自动类型推动机制(又称为砖石表达式)

package com.ccy.java.List.Collection;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class Generic02 {
    public static void main(String[] args) {
        //ArrayList<>(这里的类型会自动推断);后面的泛型可以不用写,不会报错,前提是JDK8.0
        List list = new ArrayList<>();
        //只能添加String类型的数据
        list.add("https://www.baidu.com");
        list.add("https://www.bilibili.com");
        list.add("https://www.132.com");

        //迭代器使用了泛型,只能调用String类型的中的方法,调用其他类型中的方法会报错
        Iterator i = list.iterator();
        while (i.hasNext()){
            String s = i.next();
            //截取字符串
            String substring = s.substring(8);
            System.out.println(substring);
        }
    }
}

显示结果

www.baidu.com
www.bilibili.com
www.132.com
7.3 自定义泛型
package com.ccy.java.List.Collection;


public class Generic03 {
    public void go(E o){
        System.out.println(o);
    }
    public static void main(String[] args) {
        //new对象的时候泛型指定的是String类型
        Generic03 g = new Generic03<>();
        g.go("hello");//hello
        //类型不匹配,需要的类型是字符串不是数字
        //g.go(100);
    }
}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5718275.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-18
下一篇 2022-12-18

发表评论

登录后才能评论

评论列表(0条)

保存