飞道的博客

浅谈java集合类以及示例

332人阅读  评论(0)

聊一聊java 的集合类

概述

Java中集合分为两种类型
第一种:以单个元素存储。其超级父接口是:java.util.Collection;
第二种:以键值对存储。(类似于python的集合)其超级父接口是:java.util.Map;

前者每个位置只能保存一个元素,后者可以保存两个元素。

分类

Collection又可分为List、Set、Queue
List下常用的有ArrayList、LinkedList、Vector、Stack
Set下常用的有HashSet、TreeSet
Queue又有Deque、Stack、LinkedList

SET

​ 无序不可重复,没有下标。规定Set的实例不包含重复的元素。在一个规则集内,一定不存在两个相等的元素 。

​ 实现类都不是线程安全的类,解决方案:Set set = Collections.sysnchronizedSet(Set对象);

HashSet

​ 底层是HashMap,放到HashSet集合中的元素等同于当道HashMap中的key部分。

​ 是一个用于实现Set接口的具体类,可以使用它的无参构造方法来创建空的散列集,也可以由一个现有的集合创建散列集。在散列集中,有两个名词需要关注,初始容量和客座率。客座率是确定在增加规则集之前,该规则集的饱满程度,当元素个数超过了容量与客座率的乘积时,容量就会自动翻倍。

SortedSet

无序不可重复的。但是SortedSet集合中的元素是可排序的。

无序:存进去的顺序和去除的顺序不一定相同,另外集合元素没有下标,不可重复。
可排序:可以按照大小顺序排序。

TreeSet

​ 实现了 SortedSet接口, 底层为红黑二叉树,实际上就是TreeMap,放到TreeSet集合中的元素等同于当道TreeMap中的key部分。不允许为null,不能重复,有序存储(顺序可以自定义)//存储空会报错

TreeSet的有序存储,存储元素时会判断他是否重复,并且自动排序,判断重复元素由compareTo()方法来实现。因此自定义类要使用TreeSet必须覆写Comparable接口, 如下示例

public class Demo1 {
    public static void main(String[] args) {

        Set<Person> list = new HashSet<>();
        Person per1 = new Person("p0", "s1", 21);
        list.add(per1);
        list.add(per1);
        list.add(new Person("p1", "s1", 22));
        list.add(new Person("p2", "s1", 23));
        for (Person person : list) {
            System.out.println(person);
        }
    }
}

class Person implements Comparable<Person> {
    public String name;
    public String school;
    private int age;

    public Person(String name, String school, int age) {
        this.name = name;
        this.school = school;
        this.age = age;
    }

    @Override
    public String toString() {
        return "[name: " + this.name + "    school: " + this.school + "    age: " + age + "]";
    }

    /*
     * 复写 .compareTo() 的规定: 当前对象大于传入对象,返回一个正数 当前对象等于传入对象,返回一个0 当前对象小于传入对象,返回一个负数
     */
    @Override
    public int compareTo(Person o) {
        if (this.age > o.age) {
            return 1;
        } else if (this.age < o.age) {
            return -1;
        } else {
            int i = this.name.compareTo(o.name);
            if (i != 0) {
                return i;
            }
            return this.school.compareTo(school);
        }
    }
}

LinkedHashSet

​ 底层是HashMap,LinkedHashSet是用一个链表实现来扩展HashSet类,它支持对规则集内的元素排序。HashSet中的元素是没有被排序的,而LinkedHashSet中的元素可以按照它们插入规则集的顺序提取。

Set示例

public class Demo {
    public static void main(String[] args) {

        //HashSet
        Set<String> hs = new HashSet<>();
        hs.add("a");
        hs.add("g");
        hs.add("c");
        hs.add("d");
        System.out.println("HashSet:");
        for(String cc : hs){
            System.out.print(cc+",");
        }

        System.out.println();

        //TreeSet
        Set<String> ts = new TreeSet<>();
        ts.add("a");
        ts.add("g");
        ts.add("c");
        ts.add("d");
        System.out.println("TreeSet:");
        for(String cc : ts){
            System.out.print(cc+",");
        }

        System.out.println();

        //LinkedHashSet
        Set<String> linkedHashSets = new LinkedHashSet<>();
        linkedHashSets.add("a");
        linkedHashSets.add("g");
        linkedHashSets.add("c");
        linkedHashSets.add("d");
        System.out.println("LinkedHashSet:");
        for(String cc : linkedHashSets){
            System.out.print(cc+",");
        }

    }
}

List

有下标。

可以重复,通过索引取出加入数据,顺序与插入顺序一致,可以含有null元素,

长度可变,元素存放有一定的顺序,下标从0开始。

ArrayList

底层数据结构使数组结构array,查询速度快,增删改慢,因为是一种类似数组的形式进行存储,因此它的随机访问速度极快;线程不安全,每次扩容是原来长度的1.5倍,默认容量为10, jdk源码如下

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{

private static final int DEFAULT_CAPACITY = 10;

}

初始化示例

//如何使用ArrayList创建一个List
//不指定存放的元素类型

//默认容量为10
List list = new ArrayList();

//容量为6
List list = new ArrayList(6);

扩容源代码:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6pAVipk1-1588573349667)(E:\技术帖子\笔记\基础\图\arraylist扩容.png)]

Vector

  • Vector集合底层是一个数组。
  • 初始化容量也为10,扩容之后是原来容量的两倍,而ArrayList集合是原来的1.5倍。
  • Vector中所有的方法都是线程同步的,都带有synchronized关键字,是线程安全的。(效率较低,使用较少)
    那如何将非线程安全的转换成线程安全的呢?
  • 使用集合工具类。
    1.java.util.Collections;
    2.注意java.util.Collection 是集合接口。
    但是:java.util.Collection是集合工具类。

示例

import java.util.*;

public class VectorDemo {

   public static void main(String args[]) {
      // initial size is 3, increment is 2
      Vector v = new Vector(3, 2);
      System.out.println("Initial size: " + v.size());
      System.out.println("Initial capacity: " +
      v.capacity());
      v.addElement(new Integer(1));
      v.addElement(new Integer(2));
      v.addElement(new Integer(3));
      v.addElement(new Integer(4));
      System.out.println("Capacity after four additions: " +
          v.capacity());

      v.addElement(new Double(5.45));
      System.out.println("Current capacity: " +
      v.capacity());
      v.addElement(new Double(6.08));
      v.addElement(new Integer(7));
      System.out.println("Current capacity: " +
      v.capacity());
      v.addElement(new Float(9.4));
      v.addElement(new Integer(10));
      System.out.println("Current capacity: " +
      v.capacity());
      v.addElement(new Integer(11));
      v.addElement(new Integer(12));
      System.out.println("First element: " +
         (Integer)v.firstElement());
      System.out.println("Last element: " +
         (Integer)v.lastElement());
      if(v.contains(new Integer(3)))
         System.out.println("Vector contains 3.");
      // enumerate the elements in the vector.
      Enumeration vEnum = v.elements();
      System.out.println("\nElements in vector:");
      while(vEnum.hasMoreElements())
         System.out.print(vEnum.nextElement() + " ");
      System.out.println();
   }
}

输出

Initial size: 0
Initial capacity: 3
Capacity after four additions: 5
Current capacity: 5
Current capacity: 7
Current capacity: 9
First element: 1
Last element: 12
Vector contains 3.

Elements in vector:
1 2 3 4 5.45 6.08 7 9.4 10 11 12

线程安全

        List myList = new ArrayList();
//        变成线程安全的
        Collections.synchronizedList(myList);
        myList.add("444");
        System.out.println(myList.get(0));

Stack

继承Vector

执行push时(即,将元素推入栈中),是通过将元素追加的数组的末尾中。
执行peek时(即,取出栈顶元素,不执行删除),是返回数组末尾的元素。
执行pop时(即,取出栈顶元素,并将该元素从栈中删除),是取出数组末尾的元素,然后将该元素从数组中删除

public class StackDemo {
    static void showpush(Stack<Integer> st, int a) {
        st.push(new Integer(a));
        System.out.println("push(" + a + ")");
        System.out.println("stack: " + st);
    }

    static void showpop(Stack<Integer> st) {
        System.out.print("pop -> ");
        Integer a = (Integer) st.pop();
        System.out.println(a);
        System.out.println("stack: " + st);
    }

    public static void main(String args[]) {
        Stack<Integer> st = new Stack<Integer>();
        System.out.println("stack: " + st);
        showpush(st, 42);
        showpush(st, 66);
        showpush(st, 99);
        showpop(st);
        showpop(st);
        showpop(st);
        try {
            showpop(st);
        } catch (EmptyStackException e) {
            System.out.println("empty stack");
        }
    }
}

输出结果

stack: []
push(42)
stack: [42]
push(66)
stack: [42, 66]
push(99)
stack: [42, 66, 99]
pop -> 99
stack: [42, 66]
pop -> 66
stack: [42]
pop -> 42
stack: []
pop -> empty stack

Process finished with exit code 0

LinkedList

底层用双链表实现,与前两个相比,优点是便于增删元素,缺点是单链表的存储地址不连续,插中间一个节点,需要从头节点开始查找,知道其上一个节点存储的地址才可,访问元素性能效率相对低。

(数组连续内存空间,查找速度快,增删慢;链表充分利用了内存,存储空间是不连续的,首尾存储上下一个节点的信息,所以寻址麻烦,查找速度慢,但是增删快。)

示例

LinkedList<String> lList = new LinkedList<String>();
lList.add("1");
lList.add("2");
lList.add("3");
lList.add("4");
lList.add("5");
System.out.println(lList);
lList.addFirst("0");
System.out.println(lList);
lList.addLast("6");
System.out.println(lList);

输出

[1, 2, 3, 4, 5]
[0, 1, 2, 3, 4, 5]
[0, 1, 2, 3, 4, 5, 6]

Queue

先进先出

自从Java 1.5之后,在java.util.concurrent包下提供了若干个阻塞队列,主要有以下几个:

ArrayBlockingQueue

基于数组实现的一个阻塞队列,在创建ArrayBlockingQueue对象时必须制定容量大小。并且可以指定公平性与非公平性,默认情况下为非公平的,即不保证等待时间最长的队列最优先能够访问队列。

LinkedBlockingQueue

基于链表实现的一个阻塞队列,在创建LinkedBlockingQueue对象时如果不指定容量大小,则默认大小为Integer.MAX_VALUE。

PriorityBlockingQueue

以上2种队列都是先进先出队列,而PriorityBlockingQueue却不是,它会按照元素的优先级对元素进行排序,按照优先级顺序出队,每次出队的元素都是优先级最高的元素。注意,此阻塞队列为无界阻塞队列,即容量没有上限(通过源码就可以知道,它没有容器满的信号标志),前面2种都是有界队列。

DelayQueue

基于PriorityQueue,一种延时阻塞队列,DelayQueue中的元素只有当其指定的延迟时间到了,才能够从队列中获取到该元素。DelayQueue也是一个无界队列,因此往队列中插入数据的操作(生产者)永远不会被阻塞,而只有获取数据的操作(消费者)才会被阻塞。

Map

含健值对,每个里面又可以再细分一些类可以排序,或支持并发

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5ZgdbDCw-1588573349669)(E:\技术帖子\笔记\基础\图\map.png)]

Hashtable

Hashtable继承Map接口,实现一个key-value映射的哈希表。任何非空(non-null)的对象都可作为key或者value。
  添加数据使用put(key, value),取出数据使用get(key),这两个基本操作的时间开销为常数。
Hashtable通过initial capacity和load factor两个参数调整性能。通常缺省的load factor 0.75较好地实现了时间和空间的均衡。增大load factor可以节省空间但相应的查找时间将增大,这会影响像get和put这样的操作。

HashMap

HashMap是基于哈希表的Map接口的非同步实现,继承自AbstractMap,AbstractMap是部分实现Map接口的抽象类。

在之前版本HashMap采用数组+链表实现,即使用链表处理冲突,同一hash值的链表都存储在一个链表里。但是当链表中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。而JDK1.8中,HashMap采用数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。

在HashMap中要找到某个元素,需要根据key的hash值来求得对应数组中的位置。对于任意给定的对象,只要它的hashCode()返回值相同,那么程序调用hash(int h)方法所计算得到的hash码值总是相同的。我们首先想到的就是把hash值对数组长度取模运算,这样一来,元素的分布相对来说是比较均匀的。但是,“模”运算的消耗还是比较大的,在HashMap中,(n - 1) & hash用于计算对象应该保存在table数组的哪个索引处。HashMap底层数组的长度总是2的n次方,当数组长度为2的n次幂的时候,(n - 1) & hash 算得的index相同的几率较小,数据在数组上分布就比较均匀,也就是说碰撞的几率小,相对的,查询的时候就不用遍历某个位置上的链表,这样查询效率也就较高了。

LinkedHashMap

LinkedHashMap继承自HashMap,它主要是用链表实现来扩展HashMap类,HashMap中条目是没有顺序的,但是在LinkedHashMap中元素既可以按照它们插入的顺序排序,也可以按它们最后一次被访问的顺序排序。

TreeMap

TreeMap基于红黑树数据结构的实现,键值可以使用Comparable或Comparator接口来排序。TreeMap继承自AbstractMap,同时实现了接口NavigableMap,而接口NavigableMap则继承自SortedMap。SortedMap是Map的子接口,使用它可以确保图中的条目是排好序的。

在实际使用中,如果更新图时不需要保持图中元素的顺序,就使用HashMap,如果需要保持图中元素的插入顺序或者访问顺序,就使用LinkedHashMap,如果需要使图按照键值排序,就使用TreeMap。

ConcurrentHashMap

Concurrent,并发,ConcurrentHashMap是HashMap的线程安全版。同HashMap相比,ConcurrentHashMap不仅保证了访问的线程安全性,而且在效率上与HashTable相比,也有较大的提高。

如果大家对java架构相关感兴趣,可以关注下面公众号,会持续更新java基础面试题, netty, spring boot,spring cloud等系列文章,一系列干货随时送达, 超神之路从此展开, BTAJ不再是梦想!


转载:https://blog.csdn.net/jinxinxin1314/article/details/105917205
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场