为什么集合可以自动扩充_Java Collection集合 JDK11+

工作&&学习的热情不应季节的变化而改变


1.collection 简介

        单值集合操作的最大父接口,每一次指向集合之中保存一个对象,是一种工具类,可以存储任意数量、任意类型的对象;模块在java.base ,包在java.util。

        类图如下,部分:

340e0de0ced345e6a842d46a1da9d34e.png

        List(允许重复),Set(不允许重复,按照顺序保存),SortedSet (不允许重复且排序),Queue(队列)

       Collection接口:

// 向集合中追加数据public boolean add (E e) // 向集合中追加一组数组public boolean addAll (Collection extends E> c)   // 清空集合public void clear()       // 数据查询,需要equals()方法支持  public boolean contains (Object o)   // 获取iterator 接口实例  public Iterator iterator() // 集合中删除指定元素       public boolean remove (Object o)// 返回集合中的元素个数public int size() // 将对象集合以数组的形式返回 public Object[] toArray()  

        List接口

// 将指定的元素插入此列表中的指定位置public void add (int index, E element)  // 获取此列表中指定位置的元素。public E get (int index)  // 用指定的元素替换此列表中指定位置的元素public E set (int index, E element)  // 返回指定元素在此列表中首次出现的索引;如果此列表不包含该元素,则返回-1public int indexOf (Object o)// 获取 ListIterator 接口实例public ListIterator listIterator()  // 通过指定的内容创建List集合public static  List of (E e1)   // 实现List集合排序public default void sort (Comparator super E> c)   // 返回包含任意数量元素的不可修改列表,jdk9+public static Listof(E... elements)
package cn.fz.cls;import java.util.List;public class DemoTest1 {    public static void main(String[] args) {        List all = List.of("111","222","333");  //创建不能修改的list集合    }}

        List 接口子类 ArrayList

// 使用默认容量public ArrayList()     // 设置一个初始化容量public ArrayList(int initialCapacity)

          当ArrayList 中保存的容量不足时,每一次扩充”50%”(jdk11);在每一次使用ArrayList的时候一定要考虑好长度存储问题。如果你保存的数据长度在10或以内使用无参构造即可。

          ArrayList 自定义存储对象

package cn.fz.cls;import java.util.ArrayList;import java.util.List;class Ball2 {    private String brand;    private double price;    public Ball2(String brand, double price) {        this.brand = brand;        this.price = price;    }    @Override    public String toString() {        return "Ball2{" +                "brand='" + brand + '\'' +                ", price=" + price +                '}';    }    @Override    public boolean equals(Object o) {        if (this == o) return true;        if (o == null) return false;        if (!(o instanceof Ball2)) {            return false;        }        Ball2 ball2 = (Ball2) o;        return this.brand.equals(ball2.brand) && this.price == ball2.price;    }}public class DemoTest1 {    public static void main(String[] args) {        List list = new ArrayList();        list.add(new Ball2("wn",12));        list.add(new Ball2("ss",123));        list.add(new Ball2("sbn",1234));        list.remove(new Ball2("wn",12));        System.out.println(list);    }    }

        List集合经常会用于进行数据的存储,对于需要动态查询或者删除自定义类对象里面就一定要提供equals()方法,但是如果此时只是进行存储而不进行contains()和remove()操作的时候,就不需要使用equals()方法了。

        List 接口子类 LinkedList

                基于链表的方案实现的集合存储。

// 链表实现基本代码,单向链表private class Node<T> {  private T data;  private Node next ;    public Node(T data) {    this.data = data;  }    public void addNode(Node newNode){    if (this.next == null) {      this.next = newNode;    } else {      this.next.addNode(newNode);    }  }  public void toArrayNode(){    LinkImpl.this.returnData[LinkImpl.this.foot ++] = this.data;    if (this.next != null) {      this.next.toArrayNode();    }  }  public T getNode(int index){    if (LinkImpl.this.foot ++ == index) {      return this.data;    } else {      if (this.next != null) {        return this.next.getNode(index);      } else {        return null;      }    }    }  public T setNode(int index, T newData){    if (LinkImpl.this.foot ++ == index) {      T temp = this.data;      this.data = newData;      return temp;      } else {      if (this.next != null) {        return this.next.setNode(index,newData);      } else {        return null;      }    }  }  public boolean containsNode(T data){    if (this.data.equals(data)) {      return true;    } else {      if (this.next != null) {        return this.next.containsNode(data);      } else {        return false;      }    }  }  public void removeNode(Node pre, T data) {    if (this.data.equals(data)) {      pre.next = this.next;  //空出当前节点    } else {      if (this.next != null) {  //还有后续节点        this.next.removeNode(this,data);      }    }  }}

        在LinkedList 里面提供有节点处理类,这个类定义如下:

// 这个类现在不需要外部进行访问,属于双向链表private static class Node {      E item;  存放数据    Node next; 存放下一个节点    Node prev; 存放上一个节点}

            List 接口子类vector

                整体定义和继承结构和ArrayList是一样的,这两者最大区别是Vector类中的方法采用的是synchronized同步处理机制,而ArrayList 没有采用同步。

              ArrayList与LinkedList 区别:                

                   ArrayList 是基于数组实现的集合类,而LinkedList 是基于链表实现的集合类。

                 ArrayList类 根据索引查询的时候时间复杂度O(1),而LinkedList 类 时间复杂度 O(n)      

               ArrayList与Vector区别:        

                  ArrayList 是在jdk1.2 提出集合框架的时候定义的,其内部的方法未使用同步处理,属于非线程安全的操作;

                    Vector 是在jdk1.0的时候提供的一个类,在JDK1.2之后增加到集合框架之中,所有的方法使用Synchronized同步处理机制,线程安全处理操作;

                    ArrayList 和Vector 一样都是基于数组实现的动态存储 


    Set接口

        在JDK1.9以前,Set接口并没有对Collection接口方法进行任何扩充,即:两个接口的方法是完全相同的(Set接口没有List接口中的get()方法),只不过jdk1.9开始,为其追加了一些of()方法。

package cn.fz.cls;import java.util.Set;public class DemoTest1 {    public static void main(String[] args) {        //自动排序 ,重复的抛出异常 ”IllegalArgumentException“        Set<String> all = Set.of("11111","33333","22222");        System.out.println(all);    }}

     Set接口子类HashSet(散列存储)

        HashSet里面包含有一个HashMap的结构(key=value)的偶对象,所以每当使用HashSet 无参构造的时候,HashSet 之中默认大小为16,而且每当增长到75%的时候会自动进行扩容。

      Set接口子类 TreeSet(有序存储) 

        属于排序的一种操作结构,所以里面保存的内容全部都是要求有序存储。

// 使用的是Comparable排序接口public TreeSet()   // 设置额外使用Comparable排序接口。  public TreeSet(Comparator super E> comparator)   

        一旦使用了TreeSet类保存了自定义类对象(常见的是String、包装类、系统类为主) 这个时候类中的CompareTo()方法就要将全部的属性都拿来进行比较,如果只比较了部分,并且储存的数据这部分相同的时候会认为它是重复数据。

class Ball2 implements Comparable<Ball2> {    private String brand;    private double price;    public Ball2(String brand, double price) {        this.brand = brand;        this.price = price;    }    @Override    public String toString() {        return "Ball2{" +                "brand='" + brand + '\'' +                ", price=" + price +                '}';    }    @Override    public boolean equals(Object o) {        if (this == o) return true;        if (o == null) return false;        if (!(o instanceof Ball2)) {            return false;        }        Ball2 ball2 = (Ball2) o;        return this.brand.equals(ball2.brand) && this.price == ball2.price;    }    @Override    public int compareTo(Ball2 o) {        if (this.price > o.price) {            return 1;        } else if (this.price < o.price) {            return -1;        } else {            return this.brand.compareTo(o.brand);        }    }}public class DemoTest1 {    public static void main(String[] args) {        Set all = new TreeSet();        all.add(new Ball2("wn",12));  //重复        all.add(new Ball2("ss",12));        all.add(new Ball2("sbn",12));        System.out.println(all);    }}

         消除重复元素

                Set集合最大的特点是不会进行重复元素存储,通过之前的代码可以发现TreeSet 子类依据的是Comparable 接口实现了重复元素判断,这个重复元素并不是针对所有集合类。

真正的重复元素判断实际上需要两个操作的支持:

// 进行对象编码获取public int hashCode();// 进行对象内容比较publicboolean equals(Object obj);
# 对于hashCode() 需要有一个计算公式,通过属性计算出来一个不会重复的编码,利用开发工具可以自动生成,在idea里面使用  @Overridepublic boolean equals(Object o) {    if (this == o) return true;    if (o == null || getClass() != o.getClass()) return false;    Ball2 ball2 = (Ball2) o;    return Double.compare(ball2.price, price) == 0 &&            brand.equals(ball2.brand);}@Overridepublic int hashCode() {    return Objects.hash(brand, price);}

        Iterator  迭代输出

            只要是Collection接口的子接口或者是子类都可以直接利用iterator()方法获取Iterator接口实例

                接口部分方法如下:

// 判断是有下一个内容public boolean hasNext() // 获取当前内容 public E next()  // 删除当前元素public default void remove()

        在iterator接口里面有一个remove()方法,那么它与Collection接口中定义的remove()相比有哪些区别呢?

List all = new ArrayList();all.add("1");all.add("1");all.add("3");Iterator iterator = all.iterator();while(iterator.hasNext()) {    String val = iterator.next();    if ("1".equals(val)) {        iterator.remove();    } else {        System.out.println(val);    }}

        使用Iterator接口中的remove()方法可以成功实现集合内容的删除,如果此时要使用的是Collection接口中的remove()方法。

List all = new ArrayList();all.add("1");all.add("1");all.add("3");Iterator iterator = all.iterator();while(iterator.hasNext()) {    String val = iterator.next();    if ("1".equals(val)) {        all.remove("1");    } else {        System.out.println(val);    }}

        抛异常 ConcurrentModificationException,实际上使用集合中的删除操作,在迭代的时候无法提供正常支持,所以才在iterator 接口中追加有一个remove()方法。

        从jdk1.8开始由于追加了Lambda表达式,以及方法引用和功能性接口,所以如果现在想进行内容的输出,比较简单。

package cn.fz.cls;import java.util.ArrayList;import java.util.List;public class DemoTest1 {    public static void main(String[] args) {        List<String> all = new ArrayList<String>();        all.add("1");        all.add("1");        all.add("3");        // 实际开发之中不能使用        all.forEach(System.out :: println);    }}

        Iterator接口子类ListIterator(双向迭代)

public boolean hasPrevious()  public E previous()

package cn.fz.cls;import java.util.ArrayList;import java.util.List;import java.util.ListIterator;public class DemoTest1 {    public static void main(String[] args) {        List all = new ArrayList();        all.add("1");        all.add("2");        all.add("3");        all.add("4");        all.add("5");        ListIterator listIterator = all.listIterator();        while(listIterator.hasNext()){  //正向            System.out.println(listIterator.next());        }        while(listIterator.hasPrevious()) {  //反向            System.out.println(listIterator.previous());        }    }}  

        Enumeration枚举输出

                在jdk1.0 为了方便Vector 集合输出,在Enumeration 接口最初的时候只定义有两个操作方法。

// 判断是否有下一个元素public boolean hasMoreElements()  // 获取当前元素public E nextElement()

    Iterator 接口与Enumeration 接口的核心功能非常相似,但是优点在于:Iterator是一个标准,并且方法名称简短,但是需要注意的是,并没有任何一个集合的接口拥有获取Enumeration对象方法,只有Vector这个古老的类中才有对应的方法。

package cn.fz.cls;import java.util.*;public class DemoTest1 {    public static void main(String[] args) {        Vector all = new Vector();        all.add("1");        all.add("2");        all.add("3");        all.add("4");        Enumeration enumeration = all.elements();                while (enumeration.hasMoreElements()) {            System.out.println(enumeration.nextElement());        }    }}

        foreach输出自定义类

package cn.fz.cls;import java.util.*;class Child implements Iterable<String> {    private String content[] = new String[]{"1","2","3","4"};    private int foot;    @Override    public Iteratoriterator() {        return new ChildIter();    }    private class ChildIter implements Iterator<String> {        @Override        public boolean hasNext() {            return Child.this.foot < Child.this.content.length;        }        @Override        public String next() {            return Child.this.content[Child.this.foot++];        }    }}public class DemoTest1 {    public static void main(String[] args) {        Child child = new Child();        for (String tmp : child) {  //自定义对象            System.out.println(tmp);        }    }}

        Map接口简介

            集合根据数据存储的不同分为两种形式:单值集合、二元偶对象集合,在之前使用的Collection都属于单值集合,map属于二元偶对象,所谓的二元偶对象指的是存储的数据为”key=>value” 结构对,在使用的时候可以根据key查询出相应的value的内容。

所以Collection和map存储数据的目的分别为:Collection 是为了数据的输出而存储,而Map是为了查询而存储。

             java.util.Map 是进行二元偶对象数据存储的最大父接口,在里面所有存放的内容会按照”key=value” 的形式,Map接口的常用方法如下:

public V put (K key, V value)   向集合中保存数据,如果key存在则发生替换,同时返回旧的内容,如果key不存在,返回内容为空.public V get (Object key)      通过key查询对应的内容public V remove (Object key)  根据key删除对应的内容public int size()         获取集合长度public Collection values()     返回所有的内容public Set keySet()      获取所有的key (key不能重复)public Set<Map.Entry> entrySet()    将所有的内容以Map.entry集合返回

        Map接口依赖Collection和Set接口

package cn.fz.cls;import java.util.Map;public class DemoTest1 {    public static void main(String[] args) {        //出现key重复,会出现异常        //Exception in thread "main" java.lang.IllegalArgumentException: duplicate key: k3        Mapmap = Map.of("k1",11,"k2",22,"k3",33,"k3",44);        System.out.println(map);    }}

          Map接口子类HashMap

                存储都是无序的,定义结构如下:

public class HashMapV>  extends AbstractMapV>  

                Jdk1.8之后 HashMap 算法进行了重大的变更,下边进行源码分析:

1. public HashMap() { 无参构造         this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted  }    2. staticfinal float DEFAULT_LOAD_FACTOR = 0.75f; (默认扩充阈值为”75%”)final float loadFactor;3.publicV put(K key, V value) {  return putVal(hash(key), key, value, false, true);   putVal 实现了节点的相关创建以及扩容的调用(resize())}4.resize() 默认容量大小保存16个  staticfinal int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16transient Node[] table;  默认是根据一个数组的形式存储的int threshold;  默认的大小static final int MAXIMUM_CAPACITY = 1 << 30; 最大的存储oldThr << 1  每次扩充一倍  比如  32<<1 : 64  64 << 1  1285.性能保证    staticfinal int TREEIFY_THRESHOLD = 8;   树状阈值if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st    treeifyBin(tab, hash);   //进行树状结构转换break;//每一次对于hashMap 来讲,如果要进行扩容,则表示当前的存储容量为“75%” 才会选择扩容 16*0.75 ,在jdk1.8之后如果hashMap的存储容量达到了8位,那么为了保证数据的查询性能,HashMap会将原始的链表存放结构转为红黑树结构保存。可以利用红黑树自旋的处理实现树的平衡修复.

        Map接口子类LinkedHashMap

           HashMap 之中进行数据存储的时候并不会进行顺序的定义,就可以利用LinkedHashMap 的子类来完成。

         Map接口子类TreeMap

            Java.util.TreeMap 实现的是一个排序的树结构,可以依据key的自然顺序实现排序处理。

        Map接口子类HashTable

            Jdk1.0   提出最早的偶对象存储

public class HashtableV>    extends DictionaryV>  

        HashMap与HashTable区别:

            HashTable在进行数据存储的时候是不允许存放null数据的,不管是key还是value,如果发现有null ,会出现”java.lang.NullPointerException” ,而相比较HashMap 不管是在key 还是value上都没有关于null的限制.

            HashMap(相当于ArrayList) 在进行存储的时候默认大小为16,在存储量达到8位之后 为了保证数据的查询性能使用红黑树存储,都使用了异步处理,属于非线程安全

            HashTable(相当于Vector) 在进行存储时默认大小为 11  ,HashTable 方法使用同步处理,属于线程安全的处理操作。

        Map.Entry

            存储的是二元偶对象,在Map接口里面定义有一个Map.Entry 的内部接口,此接口主要是key和value的封装的,而且在Map接口里面也可以发现Map.Entry 的子类:

Map.Entry 里面可以包装key和value ,那么也可以通过Map.Entry 获取对应的key和value,此接口定义如下:

public static interface Map.Entry  内部接口  public  K getKey()public  V getValue() // jdk1.9 之后Map接口方法:public static  Map.Entry entry (K k, V v)
package cn.fz.cls;import java.util.Map;public class DemoTest1 {    public static void main(String[] args) {        Map.Entry<String,String> entry = Map.entry("k1","v1");        System.out.println(entry.getKey() + "--" + entry.getValue());    }}

        Map.Entry 实质上定义了一个Map偶对象存储的标准,所有的Map接口的子类都依据此标准实现相应的节点数据的存储,也可以直接利用此实例实现key与value的分离。

        Iterator输出Map集合

            面对集合数据输出,肯定要考虑使用Iterator 接口完成,但是在Map接口中并没有任何一个方法可以直接获取到Iterator对象,所以此时必须经过一系列的转换得来。原因就在于它的存储不是一个普通的数据,而是一个偶对象,而Iterator 每一个都是可以输出的全部都是单个实例,为此基本的输出流程如下:

1.通过Map接口中的entrySet()方法,将Map实例转为Set接口实例public Set<Map.Entry> entrySet()2.获取了Set集合实例之后就可以调用iterator()方法获取Iterator 接口实例,泛型类型为”May.Entry”3.通过Iterator 进行迭代操作,获取每一组的”Map.Entry”实例,进行key与value的分离
package cn.fz.cls;import java.util.HashMap;import java.util.Iterator;import java.util.Map;import java.util.Set;public class DemoTest1 {    public static void main(String[] args) {        Map<String,String> map = new HashMap<>();        map.put("k1","v1");        map.put("k2","v2");        map.put("k3","v3");        Set<Map.Entry<String, String>> set  = map.entrySet();        Iterator<Map.Entry<String,String>> iterator = set.iterator();        while(iterator.hasNext()){            Map.Entry<String,String> entry = iterator.next();            System.out.println(entry.getKey() + "--" + entry.getValue());        }    }}

        从jdk1.5之后Map集合也可以使用foreach 进行输出,因为其内部实现了Iterator 接口:

package cn.fz.cls;import java.util.HashMap;import java.util.Map;public class DemoTest1 {    public static void main(String[] args) {        Mapmap = new HashMap<>();        map.put("k1","v1");        map.put("k2","v2");        map.put("k3","v3");        for (Map.Entry entry : map.entrySet() ) {            System.out.println(entry.getKey() + "--" + entry.getValue());        }    }}

        就是通过Iterator ,Map很少输出,因为主要是查询操作。

        自定义Key

            对于Map集合可以发现,设置的k和v两个泛型类型只要是引用数据类型就可以了,那么这也包括了自定义的类,也就是说自定义的类也可以称为Map中的key类型。

Map集合根据key获取数据时的流程:

1.利用hashCode ()方法生成结果进行比较,因为这只是一个数字,它的比较速度会更加的快;

2.如果发现哈希码相同的时候才会进行内容的比较。

package cn.fz.cls;import java.util.HashMap;import java.util.Map;import java.util.Objects;class Member3 {    private String name;    private int age;    public Member3(String name, int age) {        this.name = name;        this.age = age;    }    @Override    public String toString() {        return "Member3{" +                "name='" + name + '\'' +                ", age=" + age +                '}';    }    @Override    public boolean equals(Object o) {        if (this == o) return true;        if (o == null || getClass() != o.getClass()) return false;        Member3 member3 = (Member3) o;        return age == member3.age &&                name.equals(member3.name);    }    @Override    public int hashCode() {        return Objects.hash(name, age);    }}public class DemoTest1 {    public static void main(String[] args) {        Map map = new HashMap<>();        map.put(new Member3("来历",12),new String("来历"));        System.out.println(map.get(new Member3("来历",12)));    }}

        经过现在的分析可以得出一个结论:

               哈希码实际上是进行对象比较的关键所在,而进行Map存储的时候实际上也是依靠哈希码得到的存储空间,但是在很多情况下依然有可能会出现哈希冲突的问题。

          四种解决方案:

    开放定址法,链地址法,再哈希法,建立公共的溢出区,而java利用链地址法形式解决,把所有重复的内容放到一个链表之中保存(链表结构,上边有代码)。

        Queue队列简介

            队列本质指的是先进先出(First In First Out、FIFO) ,如果说现在有许多的操作者等待进行处理的时候,那么这种情况下就会将许多的操作者变为一队进行存储。

//增加队列数据public boolean add (E e)    //如果可以在不违反容量限制的情况下立即将指定的元素插入此队列public boolean offer (E e)  // 检索并删除此队列的头。public E remove()// 检索并删除此队列的头部,null如果此队列为空,则返回。public E poll()
package cn.fz.cls;import java.util.LinkedList;import java.util.Queue;public class DemoTest1 {    public static void main(String[] args) {        Queuequeue = new LinkedList<>();        queue.offer("A");        queue.offer("M");        queue.offer("C");        while (!queue.isEmpty()) {            System.out.println(queue.poll());        }    }}

        linkedList ()是基于链表的实现,所以不会改变存储数据顺序。

package cn.fz.cls;import java.util.PriorityQueue;import java.util.Queue;public class DemoTest1 {    public static void main(String[] args) {        Queuequeue = new PriorityQueue<>();        queue.offer("A");        queue.offer("M");        queue.offer("C");        while (!queue.isEmpty()) {            System.out.println(queue.poll());        }    }}

        将排序后的内容依次输出,先进先出

        Deque接口

            双向的操作能力,可以通过队首实现数据的增加与弹出,也可以在队尾上实现数据的增加与弹出。

982c4e65c7b07b54164f110205fe35df.png

package cn.fz.cls;import java.util.ArrayDeque;import java.util.Deque;public class DemoTest1 {    public static void main(String[] args) {        Dequedeque = new ArrayDeque();        deque.offerFirst("1");        deque.offerLast("3");        //从队首取数据        System.out.println(deque.poll());    }}

【完】


9163c8039a13453dfcf94fc84819cb9f.png

欲知更多精彩,请扫码关注,下期再见!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值