java集合框架及其使用

一、集合框架概述

集合、数组都是对多个数据进行存储操作的结构,简称Java容器。这里的存储,主要指的是内存层面的存储,不涉及到持久化的存储。

数组的特点及缺点

1 数组在存储多个数据方面的特点:

  • 一旦初始化以后,其长度就确定了
  • 数组一旦定义好,其元素的类型也就确定了。我们也就只能操作指定类型的数据了。比如:String[]arr;int[]arr1;Object[]arr2;

2 数组在存储多个数据方面的缺点:

  • 一旦初始化以后,其长度就不可修改。
  • 数组中提供的方法非常有限,对于添加、删除、插入数据等操作,非常不便,同时效率不高。
  • 获取数组中实际元素的个数的需求,数组没有现成的属性或方法可用
  • 数组存储数据的特点:有序、可重复。对于无序、不可重复的需求,不能满足。

Java 集合可分为Collection和Map两种体系

Collection接口:单列数据,定义了存取一组对象的方法的集合

  • List:元素有序、可重复的集合

  • Set:元素无序、不可重复的集合

在这里插入图片描述

Map接口:双列数据,保存具有映射关系“key-value对”的集合接口:双列数据,保存具有映射关系“key-value对”的集合

在这里插入图片描述

二、Collection接口

|----CoLlection接口:单列集合,用来存储一个一个的对象
	|----List接口:存储有序的、可重复的数据。--> “动态”数组,替换原有的数组
		|----ArralList:作List接口的主要实现类;线程不安全的,效率高;底层使用Object[]存储。适合改、查
		|----LinkedList:底层使用双向链表存储;适合增、删 
		|----Vector:作为为ist接口的古老(JDK1.0)实现类;线程安全的,效率低;底层使用Object[]存储
	|----Set接口:存储无序(元素存储顺序与添加顺序不同,而由元素哈希值决定)的、不可重复的数据
		|----HashSet:作为set接口的主要实现类;线程不安全的;可以存储null值
			|----LinkedHashSet:作为HashSet的子类;遍历其内部教据时,可以按照添加的顺序遍历
		|----TreeSet:可以按照添加对象的指定属性,进行排序。

关于迭代器Iterator

Modifier and TypeMethod and Description
default voidforEachRemaining(Consumer<? super E> action)
Performs the given action for each remaining element until all elements have been processed or the action throws an exception.
booleanhasNext()
Returns true if the iteration has more elements.
Enext()
Returns the next element in the iteration.
default voidremove()
Removes from the underlying collection the last element returned by this iterator (optional operation).

lterator对象称为迭代器(设计模式的一种),主要用于遍历Collection集合中的元素

GOF给迭代器模式的定义为:提供一种方法访问一个容器(container)对象中各个元素,而又不需暴露该对象的内部细节。迭代器模式,就是为容器而生。类似于“公交车上的售票员”、“火车上的乘务员”、“空姐”。

Collection接口继承了java.lang.lterable接口,该接口有一个iterator()方法,那么所o有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了Iterator接口的对象。

lterator仅用于遍历集合,lterator本身并不提供承装对象的能力。如果需要创建Iterator对象,则必须有一个被迭代的集合。

集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合的第一个元素之前。

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

public class IteratorTest {
    public static void main(String[] args) {
        Collection<String> collection = new ArrayList<>();
        collection.add("jack");
        collection.add("lisa");
        collection.add("tom");
        collection.add("lucy");
        Iterator iterator = collection.iterator();
        //hasNext():判断是否还有下一个元素
        while (iterator.hasNext()) {
            //next():①指针下移②将下移以后集合位置上的元素返回
            String name = (String) iterator.next();
            if ("tom".equals(name)) {
                // 1 Iterator可以删除集合的元素,但是是遍历过程中通过迭代器对象的remove方法,不是集合对象的remove方法。
                // 2 如果还未调用next()或在上一次调用next()方法之后已经调用了remove方法,再调用remove都会报IllegalStateException。
                iterator.remove();
            }
            System.out.println(name);
        }

        System.out.println("******************");
        Iterator iterator1 = collection.iterator();
        while (iterator1.hasNext()) {
            System.out.println(iterator1.next());
        }
    }
}

List 接口

  • 鉴于Java中数组用来存储数据的局限性,我们通常使用List替代数组
  • List集合类中元素有序、且可重复,集合中的每个元素都有其对应的顺序索引
  • List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素
  • JDK API中List接口的实现类常用的有:ArrayList、LinkedList和Vector。

1 ArrayList

ArrayList是List接口的典型实现类、主要实现类。本质上,ArrayList是对象引用的一个”变长”数组。

在这里插入图片描述

以下是jdk8的api中ArrayList的构造函数描述:

Constructor and Description
ArrayList()Constructs an empty list with an initial capacity of ten.
ArrayList(Collection<? extends E> c)Constructs a list containing the elements of the specified collection, in the order they are returned by the collection’s iterator.
ArrayList(int initialCapacity)Constructs an empty list with the specified initial capacity.

jdk7情况下:当调用无参构造函数时,Arraylist list = new Arraylist(),底层创建了初始长度是10的Object[]数组elementData,当向数组中添加元素超过数组最大容量时,则elementData数组扩容。默认情况下,扩容为原来的容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中。实际使用时建议用带参的构造器:Arraylist list=new Arraylist(int capacity),避免扩容操作影响效率。

jdk8情况下:当调用无参构造函数时,Arraylist list = new Arraylist(),底层并不会创建数组,数组为空,当第一次向数组中添加元素时,才会为数组分配内存,且数组初始容量为10(jdk8中注释没改,但是代码逻辑改了),当数组容量不足时,也是扩容为原来容量的1.5倍:newCapacity = oldCapacity + (oldCapacity >> 1)

    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    public ArrayList() {
            this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

	private int size;
	private static final int DEFAULT_CAPACITY = 10;

	public boolean add(E e) {
    	ensureCapacityInternal(size + 1);  // 第一步: size 为int,初始为0,ensureCapacityInternal(1)
    	elementData[size++] = e;
    	return true;
	}

    private void ensureCapacityInternal(int minCapacity) {
        // 第二步:调用无参构造器时,this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA,又此方法实参为 1
        //		  因此calculateCapacity(DEFAULTCAPACITY_EMPTY_ELEMENTDATA, 1)
        
        // 第四步:执行 ensureExplicitCapacity(10)
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

	private static int calculateCapacity(Object[] elementData, int minCapacity) {
        // 第三步:if条件为真,return 10(DEFAULT_CAPACITY = 10)
    	if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // 第五步:10 - 0 > 0,条件成立,执行grow(10)
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

    private void grow(int minCapacity) {				 // 方法实参为10,minCapacity == 10
        int oldCapacity = elementData.length;			  // elementData.length == 0 --> oldCapacity = 0
        int newCapacity = oldCapacity + (oldCapacity >> 1);// newCapacity = 0 + 0 (>>:算术右移运算符,也称带符号右移,右移一次相当于除2)
        if (newCapacity - minCapacity < 0)				  // 0 - 10 < 0,if条件为真,newCapacity = 10
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        
        elementData = Arrays.copyOf(elementData, newCapacity);// 创建一个初始容量为10的数组
    }

总结:jdk7 中ArrayList的对象的创建类似于单例的饿汉式,而jdk8中的ArrayList的对象的创建类似于单例的懒汉式,延迟了数组的创建,节省内存。

2 LinkedList

LinkedList:双向链表,内部没有声明数组,而是定义了Node类型的first和last,用于记录首末元素。同时,定义内部类Node,作为LinkedList中保存数据的基本结构。Node除了保存数据,还定义了两个变量:

  • prev变量记录前一个元素的位置
  • next变量记录下一个元素的位置
	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;
        }
    }

在这里插入图片描述

Constructor and Description
LinkedList()Constructs an empty list.
LinkedList(Collection<? extends E> c)Constructs a list containing the elements of the specified collection, in the order they are returned by the collection’s iterator.

当调用无参构造函数时,什么也不做,向链表中添加元素时,才会创建链表。

	transient int size = 0;

    /**
     * Pointer to first node.
     */
    transient Node<E> first;

    /**
     * Pointer to last node.
     */
    transient Node<E> last;
	
	/**
     * Constructs an empty list.
     */
    public LinkedList() {			// 第一步:创建一个LinkedList			
    }
    
    public boolean add(E e) {		// 第二步:添加第一个元素 E e,执行 linkLast(e)
        linkLast(e);
        return true;
    }
    
    /**
     * Links e as last element.
     */
    void linkLast(E e) {			// 第三步:将元素 E e 添加到链表末尾
        final Node<E> l = last;		 // 初始last == null --> l = null
        final Node<E> newNode = new Node<>(l, e, null);// newNode = new Node<>(null, e, null)
        last = newNode;				// last = newNode
        if (l == null)
            first = newNode;		 // first = newNode
        else
            l.next = newNode;
        size++;						// size == 1
        modCount++;
    }				// 方法执行结束,此时 头节点first、尾节点last 为同一节点,first == last;链表长度 size == 1
    
    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;
        }
    }

3 Vector

现在已经不经常使用了,略过。

4 List接口常用方法

用处具体方法
void add(Object ele):末尾添加一个元素
object remove(int index):移除指定index位置的元素,并返回此元素
object set(int index,Object ele):设置指定index位置的元素为ele
object get(int index):获取指定index位置的元素
int indexOf(Object obj):返回obj在集合中首次出现的位置
int LastIndexOf(Object obj):返园obj在当前集合中最后一次出现的位置
List sublist(int fromIndex,int toIndex):返回从fromIndex 到toIndex位置的子集合
插入void add(int index,Object ele):在index位置插入ele元素
boolean addAll(int index,Collection eles):从index位置开始将eles 中的所有元素添加进来
遍历Iterator迭代器
增强for循环(其底部也是用Iterator实现)
普通循环:for、while
获取长度int size():返回集合长度

Set接口

  • Set接口是Collection的子接口,set接口没有提供额外的方法
  • Set集合不允许包含相同的元素,如果试把两个相同的元素加入同一个Set集合中,则添加操作失败
  • Set判断两个对象是否相同不是使用==运算符,而是根据 equals()方法

Set中添加元素的过程—以HashSet为例

向HashSet 中添加元素a,首先得用元素a所在类的的HashCode()方法,计算元素a的哈希值,此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置(即为:索引位置),判断数组此位置上是否已经有元素:

  • 如果此位置上没有其他元素,则元a添加成功
  • 如果此位置上有其他元素b(或以链表形式存在的多个元素),则比较元素和与元素和的hash值:
    • 如果hash值不相同,则元a添加成功。
    • 如果hash值相同,进而需要调用元素a所在类的equals()方法:
      • equals()返回true,元a添加失败
      • equals()返false,则元素a添加成功

1 HashSet

HashSet是Set接口的典型实现,大多数时候使用Set集合时都使用这个实现类。HashSet按Hash算法来存储集合中的元素,因此具有很好的存取、查找、删除性能。HashSet具有以下特点:

  • 不能保证元素的排列顺序
  • HashSet不是线程安全的
  • 集合元素可以是null

HashSet 集合判断两个元素相等的标准:两个对象通过hashCode()方法比较相等,并且两个对象的equals()方法返回值也相等。

对于存放在Set容器中的对象,对应的类一定要重写equals()和hashCode(Object obj)方法,以实现对象相等规则。即:“相等的对象必须具有相等的散列码”。

在这里插入图片描述

2 LinkedHashSet

LinkedHashSet是HashSet的子类。LinkedHashSet 根据元素的hashCode值来决定元素的存储位置,但它同时使用双向链表维护元素的次序,这使得元素看起来是以插入顺序保存的。LinkedHashSet插入性能略低于HashSet,但在迭代访问Set里的全部元素时有很好的性能。LinkedHashSet不允许集合元素重复。

在这里插入图片描述

3 TreeSet

在这里插入图片描述

TreeSet是SortedSet接口的实现类,TreeSet可以确保集合元素处于排序状态。默认情况下,TreeSet采用自然排序。TreeSet底层使用红黑树结构存储数据,其新增的方法如下:(了解)

Comparator comparator()
Object first()
Object last()
Object lower(Objecte)
Object higher(Objecte)
SortedSet subSet(fromElement,toElement)
SortedSet headSet(toElement)
SortedSet tailSet(fromElement)

TreeSet 两种排序方法:自然排序和定制排序。自然排序中,比较两个对象是否相同的标准为:compareTo()返回0。不再是equals()

import java.util.Arrays;
import java.util.Comparator;
import java.util.TreeSet;

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

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

    // 省略Getter、Setter、toString

    @Override
    public int compareTo(Person o) {
        // 先按姓名升序排序,再按年龄升序排序
        if (this.getName().equals(o.getName())) {
            return Integer.compare(this.getAge(), o.getAge());
        } else {
            return this.getName().compareTo(o.getName());
        }
    }
}

class Good {
    private String name;
    private int price;

    public Good(String name, int price) {
        this.name = name;
        this.price = price;
    }

    // 省略Getter、Setter、toString
}
public class TreeSetTest {
    public static void main(String[] args) {
        // 1 自然排序
        Person[] peoples = new Person[4];
        peoples[1] = new Person("jack", 12);
        peoples[2] = new Person("jim", 8);
        peoples[3] = new Person("lisa", 66);
        peoples[0] = new Person("jack", 25);
        TreeSet<Person> peopleTreeSet = new TreeSet<>();
        peopleTreeSet.addAll(Arrays.asList(peoples));
        System.out.println(peopleTreeSet);

        // 2 定制排序
        Comparator<Good> comparator = new Comparator<Good>() {
            @Override
            public int compare(Good o1, Good o2) {
                // 先按价格降序排序,再按商品名升序排序
                if (o1.getPrice() == o2.getPrice()) {
                    return o1.getName().compareTo(o2.getName());
                } else {
                    return Integer.compare(o2.getPrice(), o1.getPrice());
                }
            }
        };
        Good[] goods = new Good[4];
        goods[0] = new Good("apple", 15);
        goods[1] = new Good("orange", 88);
        goods[2] = new Good("banana", 15);
        goods[3] = new Good("watermelon", 77);
        TreeSet<Good> goodsTreeSet = new TreeSet<>(comparator);
        goodsTreeSet.addAll(Arrays.asList(goods));
        System.out.println(goodsTreeSet);
    }
}

三、Map接口

Map<key, value> :其中key–无序的、不可重复的,使用Set 存储所有的key ;其中value–无序的、可重复的,使用Collection存储所有的value。一个键值对:key-value构成了一个Entry对象。Map 中的entry:无序的、不可重复的,使用Set 存储所有的entry

|----Map:双列数据,存储key-value对的数据
	|----HashMap:作为Map的主要实现类;线程不安全的,效率高;能存储null的key和value。
		|----LinkedHashMap:保证在遍历map元素时,可以按照添加的顺序实现遍历。原因:在原有lashMap底层结构基础上,添加了一对							   指针,指向前一个和后一个元素。对于频繁的遍历操作,此类执行效率高于HashMap。
	|----TreeMap:保证按照添加的key-value对进行排序,实现排序遍历。此时考虑key的自然排序或定制排序底层使用红黑树
	|----Hashtable:作为古老(jdk1.0)的实现类;线程安全的,效率低;不能存储null的key 和和alue
		|----Properties:常用来处理配置文件。key 和value 都是String类到

1 HashMap

  • HashMap是Map接口使用频率最高的实现类
  • 允许使用null键和null值,与HashSet一样,不保证映射的顺序
  • 所有的key构成的集合是Set:无序的、不可重复的。所以,key所在的类要重写:equals()和hashCode()
  • 所有的value构成的集合是Collection:无序的、可以重复的。所以,value所在的类要重写:equals()
  • 一个key-value构成一个entry
  • 所有的entry构成的集合是Set:无序的、不可重复的
  • HashMap 判断两个key相等的标准是:两个key通过equals()方法返回true,hashCode值也相等。
  • HashMap 判断两个value相等的标准是:两个value通过equals()方法返回true。

HashMap的底层实现原理–以jdk7为例说明

HashMap map = new HashMap():在实例化以后,底层创建了长度是16的一维数组Entry[] table。当执行map.put(key1, value1)方法时:

  • 首先调用key1所在类的hashcode()计算key1哈希值,此哈希值经过某种算法计拿以后,得到在Entry数组中的存放位置。如果此位置上的数据为空,此时的key1-value1添加成功。
  • 如果此位置上的数据不为空,(意味着此位置上存在一个或多个数据(以链表形式存在)),比较key1和已经存在的一个或多个数据的哈希值:
    • 如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1添加成功。
    • 如果key1的哈希值和已经存在的某一个数据(key2-value2)的哈希值相同,继续比较:调用key1所在类的equals(key2)
      • 如果equals()返回false:此时key1-value1添加成功。
      • 如果equals()返回true:使用value1替换value2。

在不断的添加过程中,会涉及到扩容问题,默认的扩容方式:扩容为原来容量的2倍,并将原有的数据复制过来。

在这里插入图片描述

jdk8 相较于jdk7在底层实现方面的不同

  1. new HashMap():底层没有创建一个长度为16的数组
  2. jdk8底层的数组是:Node[],而非Entry[]
  3. 首次调用put()方法时,底层创建长度为16的数组
  4. jdk7底层结构只有:数组+链表。jdk8中底层结构:数组+链表+红黑树。当数组的某一个索引位置上的元素以链表形式存在的数据个数 > 8 且当前数组的长度 > 64 时,此时此索引位置上的所有数据改为使用红黑树存储。

在这里插入图片描述

2 LinkedHashMap

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

3 TreeMap

保证按照添加的key-value对进行排序,实现排序遍历。此时考虑key的自然排序或定制排序底层使用红黑树

4 Properties

Properties类是Hashtable的子类,该对象用于处理属性文件。由于属性文件里的key、value都是字符串类型,所以Properties里的key和value都是字符串类型。存取数据时,建议使用setProperty(String key,String value)方法和getProperty(String key)方法

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/dbname?serverTimezone=UTC
jdbc.user=root
jdbc.password=*****
import java.io.FileInputStream;
import java.util.Properties;

public class PropertiesTest {
    public static void main(String[] args) throws Exception {
        Properties props = new Properties();
        FileInputStream fis = new FileInputStream("jdbcConfig.properties");
        props.load(fis);

        String driver = props.getProperty("jdbc.driver");
        String url = props.getProperty("jdbc.url");
        String user = props.getProperty("jdbc.user");
        String password = props.getProperty("jdbc.password");
        System.out.println(driver);
        System.out.println(url);
        System.out.println(user);
        System.out.println(password);
        
        fis.close();
    }
}

5 Map接口常用方法

添加、删除、修改操作

Object put(Object key,Object value):将指定key-value添加到(或修改)当前map对象中
void putAll(Map m):将m中的所有key-value对存放到当前map中
Object remove(Object key):移除指定key的key-value对,并返回value
void clear():清空当前map中的所有数据

元素查询的操作

Object get(Object key):获取指定key对应的value
boolean containsKey(Object key):是否包含指定的key
boolean containsValue(Object value):是否包含指定的value
int size():返回map中key-value对的个数
boolean isEmpty():判断当前map是否为空
boolean equals(Object obj):判断当前map和参数对象obj是否相等

元视图操作的方法

Set keySet():返回所有key构成的Set集合
Collection values():返回所有value构成的Collection集合
Set entrySet():返回所有key-value对构成的Set集合

四、Collections工具类

Collections 是一个操作Set、List和Map等集合的工具类。Collections中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作,还提供了对集合对象设置不可变、对集合对象实现同步控制等方法。

排序操作:(均为static方法)

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对象的所有旧值

Collections类中提供了多个synchronizedXxx()方法,该方法可使将指定集合包装成线程同步的集合,从而可以解决多线程并发访问
集合时的线程安全问题

static <T> Collection<T>synchronizedCollection(Collection<T> c)
返回由指定集合支持的同步(线程安全)集合。
static <T> List<T>synchronizedList(List<T> list)
返回由指定列表支持的同步(线程安全)列表。
static <K,V> Map<K,V>synchronizedMap(Map<K,V> m)
返回由指定的Map支持的同步(线程安全)Map。
static <K,V> NavigableMap<K,V>synchronizedNavigableMap(NavigableMap<K,V> m)
返回指定的导航Map支持的同步(线程安全)导航Map。
static <T> NavigableSet<T>synchronizedNavigableSet(NavigableSet<T> s)
返回由指定的导航集支持的同步(线程安全)导航集。
static <T> Set<T>synchronizedSet(Set<T> s)
返回一个由指定集合支持的同步(线程安全)集。
static <K,V> SortedMap<K,V>synchronizedSortedMap(SortedMap<K,V> m)
返回一个由指定的排序映射支持的同步(线程安全)排序的Map。
static <T> SortedSet<T>synchronizedSortedSet(SortedSet<T> s)
返回一个由指定的排序集支持的同步(线程安全)排序集。
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值