Java集合

Java集合

层次一:选择合适的集合类去实现数据的保存,调用其内部的相关方法。

层次二:不同的集合类底层的数据结构为何?如何实现数据的操作的:增删改查等。



前言

集合与数组存储的概述

集合、数组都是对多个数据进行存储操作的结构,简称Java容器。

说明:此时的存储,主要指的是内存层面的存储,不涉及到持久化的存储(.txt,.jpg,.avi,数据库中)

数组的存储特点

缺点:
1、数组一旦定义好了,长度就确定了,不易扩展。
2、对于添加、删除、插入等操作,非常不便,同时效率不高。
3、 有序可重复,对于无序、不可重复 不能满足
优点:
数组一旦定义好了,基本数据存储类型确定。只能操作指定的类型数据。(优点,不易出现多种类型数据,操作复杂)

如 String[] arr; int [] arr; object[] arr;

一、集合框架

主要分为Collection 和 Map
Collection 接口:单列集合,用来存储一个个对象
----- List接口:有序、可重复数据—》动态数组
----- ----- ArrayList、 LinkedList 、 Vector
----- Set接口:存储无序的、不可重复
---------- HashSet、LinkedHashSet、TreeSet
-----Map接口:双列数据,用来存储一对,<key,value>
---------- HashMap、 LinkedHashMap 、 TreeMap、Hashtable、 Properties

在这里插入图片描述

1、Collection 接口常用方法

 public void test() {
//        Coolection--> List --->ArrayList
        Collection coll = new ArrayList();
        coll.add("AA");
        coll.add(123);//自动装箱 int --integer
        coll.add(new Date());
        System.out.println(coll);
        Collection<Integer> coll2 = new ArrayList<Integer>();
        coll2.add(12);
        coll2.add(34);

//        coll.add(coll2);
//        System.out.println(coll);//[AA, 123, Sun May 23 20:52:24 CST 2021, [12, 34]]
        coll.addAll(coll2);
        System.out.println(coll); //[AA, 123, Sun May 23 20:56:49 CST 2021, 12, 34]
        coll.remove(12);
//        每次都会调用equals方法
        System.out.println(coll);//[AA, 123, Sun May 23 20:56:49 CST 2021, 34]

        coll.add(new Person("ma", 123));
//        这里需要重写toString 才能展示
        System.out.println(coll);
//        这里需要重新equals方法,才能找到,否则无法移除Person,调用了coll.size()
        coll.remove(new Person("ma", 123));
        System.out.println(coll);

//        retainAll(Collection coll1) 返回交集
        //7.hashCode():返回当前对象的哈希值
        System.out.println(coll.hashCode());

        //8.集合 --->数组:toArray()
        Object[] arr = coll.toArray();
        for(int i = 0;i < arr.length;i++){
            System.out.println(arr[i]);
        }

        //拓展:数组 --->集合:调用Arrays类的静态方法asList()
        List<String> list = Arrays.asList(new String[]{"AA", "BB", "CC"});
        System.out.println(list);

        List arr1 = Arrays.asList(new int[]{123, 456});
        System.out.println(arr1.size());//1

        List arr2 = Arrays.asList(new Integer[]{123, 456});
        System.out.println(arr2.size());//2

        //9.iterator():返回Iterator接口的实例,用于遍历集合元素。放在IteratorTest.java中测试
        
    }

2、Collection 遍历方法

Iterator 是设计模式的一种;
最初的iterator 指向空,先判断terator.hasNext(),再取出iterator.next()以保证并不会出现空指针。

   @Test
    public void test2(){
//        Iterator

        Collection coll = new ArrayList();
        coll.add(122);
        coll.add("sasa");
        coll.add("2133");
        coll.add(new Person("as", 123));
        Iterator iterator = coll.iterator();
        while( iterator.hasNext()){
            System.out.println(iterator.next());
        }
        System.out.println("###################");
//        foreach
        for (Object o: coll)
        {
            System.out.println(o);
        }
    }

3、Collection 子接口List

List常用方法

增:add(Object obj)
删:remove(int index) / remove(Object obj)
改:set(int index, Object ele)
查:get(int index)
插:add(int index, Object ele)
长度:size()
遍历:① Iterator迭代器方式
② 增强for循环
③ 普通的循环

实现List常用类(源码分析)

  • ArrayList:作为List接口的主要实现类;线程不安全的,效率高;底层使用Object[] elementData存储
  • LinkedList:对于频繁的插入、删除操作,使用此类效率比ArrayList高;底层使用双向链表存储
  • Vector:作为List接口的古老实现类;线程安全的,效率低;底层使用Object[] elementData存储

面试题

ArrayList、LinkedList、Vector者的异同?

同: 三个类都是实现了List接口,存储数据的特点相同:存储序的、可重复的数据
不同: 见上

4、Collection 子接口Set

Set中常用方法基本都在Collection中,没有特地定制方法。

  1. 无序性:不等于随机性。存储的数据在底层数组中并非照数组索引的顺序添加,而是根据数据的哈希值决定的。
  2. 不可重复性:保证添加的元素照equals()判断时,不能返回true.即:相同的元素只能添加一个。

HashSet

Set中添加元素过程:
当我们添加元素A时,首先通过元素A所在类hashcode()方法计算A的hash值所在位置,然后通过某种算法(如对16取余数等)计算哈希值对应Set底层数组所在位置。
1、 该位置上不存在元素,则添加成功
2、该位置上有元素B(或以链表形式存在的多个元素,则比较元素A与元素B的hash值:
如果hash值不相同,则元素a添加成功。
3、如果hash值相同,进而需要调用元素A所在类的equals()方法:
equals()返回true,元素A添加失败
equals()返回false,则元素A添加成功.

对于添加成功的情况2和情况3而言:元素a 与已经存在指定索引位置上数据以链表的方式存储。
jdk 7 :元素a放到数组中,指向原来的元素。
jdk 8 :原来的元素在数组中,指向元素a
总结:七上八下

HashSet底层:数组+链表的结构

HashSet/LinkedHashSet

向Set(主要指:HashSet、LinkedHashSet)中添加的数据,其所在的类一定要重写hashCode()和equals()
要求:重写的hashCode()和equals()尽可能保持一致性:相等的对象必须具有相等的散列码

  • 重写两个方法的小技巧:对象中用作 equals() 方法比较的 Field,都应该用来计算 hashCode 值。
TreeSet

1.自然排序中,比较两个对象是否相同的标准为:compareTo()返回0.不再是equals().
2.定制排序中,比较两个对象是否相同的标准为:compare()返回0.不再是equals().

 public void test3(){
//        自然的排序、定制排序

        Comparator com = new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                if (o1 instanceof Person && o2 instanceof Person){
                    Person p1 = (Person) o1;
                    Person p2 = (Person) o2;
                    return p1.getName().compareTo(p2.getName());
                    String
                }

                return 0;
            }
        };

        TreeSet set = new TreeSet(com);
        set.add(new Person("马云",32));
        set.add(new Person("马话题",13));
        set.add(new Person("张三",34));
        set.add(new Person("以实",12));
        set.add(new Person("中国",100));

//       在person类中重写的方法
//        @Override
//        public int compareTo(Object o) {
//            if(o  instanceof Person){
//                Person p = (Person) o;
//                return p.name.compareTo(this.name);
//            }
//            return 0;
//        }
//    }

        Iterator iterator = set.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }


    }

5、Map

Map–双列数据,通过key-value,来进行存储。

  1. HashMap,作为Map的主要实现类,线程不安全,但效率高;可存储null的key-value。

LinkedHashMap,继承HashMap类:保证在遍历map元素的同时,可以依照添加的顺序实现遍历。 原因:在原本HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素。对于频繁遍历操作,此类执行效率高HashMap。

  1. TreeMap
    保证照添加的key-value对进行排序,实现排序遍历。此时考虑key的自然排序或定制排序—底层使用红黑树。
  2. Hashtable
    较老的实现类,线程安全,效率低;不能存储nulll的key和value

Properties继承Hashtable:常用来处理配置文件。key和value都是String类型

总结:HashMap底层:数组+链表(7之前) 数组+ 链表+红黑树 (8)

Map的存储结构的理解

Map的key是无序的、不可重复的,使用Set存储key–>key所在的类要重写equals、hashcode方法。

Map中的value:无序的、可重复的,使用Collection存储所的value —>value所在的类要重写equals()。

一个键值对:key-value构成了一个Entry对象。

Map中的entry:无序的、不可重复的,使用Set存储所以entry

HashMap 实现原理

HashMap map= new HashMap()

jdk7
在实例化后,HashMap底层创建了长度为16的Entry[] table.
在针对有数据的map后,进行map.put(key1,value1);
首先调用key1所在类别的hashcode(),计算key1的哈希值,此哈希值经过某种算法计算以后,获得在Entry数组中存放位置,分为以下情况:

  • 如果存放位置数据为空,则key1-value1添加成功
  • 如果数据不为空,则意味着需要存储多个数据,链表形式,
    1、计算key1的哈希值与已存在数据的哈希值进行比较,不同可添加成功;
    2、如果哈希值与某个数据相同,则继续比较,通过所在类的euqals方法比较,返回false–添加成功;返回true则替换当前相同值。

扩容问题:
在不断的添加过程中,会涉及到扩容问题,当超出临界值(且要存放的位置非空)时,扩容。默认的扩容方式:扩容为原来容量的2倍,并将原的数据复制过来。

jdk8

  1. 首先在实例化后没有立刻创建长度为16的数组,在进行添加数据时,才开始创建。
  2. jdk8 底层数组Node[] ,不是Entry[].
  3. 首次调用put方法时,底层创建长度为16的数组,
  4. jdk8底层结构增加了红黑树,为数组+链表+红黑树
  5. 形成链表时,七上八下的结构
  6. 当数组的某一个索引位置上的元素以链表形式存在的数据个数 > 8 且当前数组的长度 > 64时,此时此索引位置上的所数据改为使用红黑树存储。

DEFAULT_INITIAL_CAPACITY : HashMap的默认容量,16
DEFAULT_LOAD_FACTOR:HashMap的默认加载因子:0.75 threshold:扩容的临界值,=容量*填充因子:16 *
0.75 => 12 TREEIFY_THRESHOLD:Bucket中链表长度大于该默认值,转化为红黑树:8 MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量:64

LinkedHashMap 实现原理

LinkedHashMap底层使用的结构与HashMap相同,因为LinkedHashMap继承于HashMap.
区别就在于:LinkedHashMap内部提供了Entry,继承了HashMap中的Node.

TreeMap

TreeMap区别在于,添加的key必须由同一个类创建的对象,因为要通过key进行排序:自然排序、定制排序。

Properties读取配置文件

Properties:常用来处理配置文件。key和value都是String类型

public static void main(String[] args)  {
    FileInputStream fis = null;
    try {
        Properties pros = new Properties();

        fis = new FileInputStream("jdbc.properties");
        pros.load(fis);//加载流对应的文件

        String name = pros.getProperty("name");
        String password = pros.getProperty("password");

        System.out.println("name = " + name + ", password = " + password);
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if(fis != null){
            try {
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

Collections 工具类

操作集合的工具类,Map和Collection

常用方法

reverse(List):反转 List 中元素的顺序 shuffle(List):对 List 集合元素进行随机排序
sort(List):根据元素的自然顺序对指定 List 集合元素升序排序 sort(List,Comparator):根据指定的
Comparator 产生的顺序对 List 集合元素进行排序 swap(List,int, int):将指定 list 集合中的 i处元素和 j 处元素进行交换 Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素 Object
max(Collection,Comparator):根据 Comparator 指定的顺序,返回给定集合中的最大元素 Object
min(Collection) Object min(Collection,Comparator) int
frequency(Collection,Object):返回指定集合中指定元素的出现次数 void copy(List dest,List
src):将src中的内容复制到dest中 boolean replaceAll(List list,Object
oldVal,Object newVal):使用新值替换 List 对象的所旧值

说明:ArrayList和HashMap都是线程不安全的,如果程序要求线程安全,我们可以将ArrayList、HashMap转换为线程的。
使用synchronizedList(List list) 和 synchronizedMap(Map map)

总结

jdk8:
ArrayList :通过Object[] elmentData数组,调用空参构造器,没有指定长度(jdk7创建直接创建10的数组)
调用add方法后,创建大小10的数组,并通过size计算当前存在元素。元素存储满了,通过1.5倍扩张,并进行复制到创建数组。

LinkedList:实现了Node first和 last,node中

 private static class Node<E> {
        E item;
        Node<E> next;
        Node<E> prev;

        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }

Vector的源码分析:jdk7和jdk8中通过Vector()构造器创建对象时,底层都创建了长度为10的数组。在扩容方面,默认扩容为原来的数组长度的2倍。

调用了HashMap进行搭建,Set(主要指:HashSet、LinkedHashSet)中添加的数据,其所在的类一定要重写hashCode()和equals()

要求:重写的hashCode()和equals()尽可能保持一致性:相等的对象必须具有相等的散列码

HashMap 使用的Node<k,v>

static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        V value;
        Node<K,V> next;

        Node(int hash, K key, V value, Node<K,V> next) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值