Java-集合

Posted 旭东东ღゝ◡╹)ノ♡

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java-集合相关的知识,希望对你有一定的参考价值。

一、Java集合框架概述

●一方面,面向对象语言对事物的体现都是以对象的形式,为了方便对多个对象的操作,就要对对象进行存储。另一方面,使用Array存储对象方面具有一些弊端,而Java集合就像一种容器,可以动态地把多个对象的引用放入容器中。

➢数组在内存存储方面的特点:

数组初始化以后,长度就确定了。
数组声明的类型,就决定了进行元素初始化时的类型

➢数组在存储数据方面的弊端:

数组初始化以后,长度就不可变了,不便于扩展
数组中提供的属性和方法少,不便于进行添加、删除、插入等操作,且效率不高。同时无法直接获取存储元素的个数
数组存储的数据是有序的、可以重复的。--->存储数据的特点单一

●Java集合类可以用于存储数量不等的多个对象,还可用于保存具有映射关系的关联数组。

二、集合框架

●Java集合可分为CollectionMap两种体系

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

List:元素有序、可重复的集合 --> “动态”数组

ArrayList、LinkedList、Vector

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

HashSet、LinkedHashSet、TreeSet

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

HashMap、LinkedHashMap、TreeMap、Hashtable、Properties

Collection接口继承树

Map接口继承树

三、Collection接口

1.Collection接口常用的15种方法

package com.xudong.java;

import org.junit.Test;

import java.util.*;

public class CollectionTest {
    @Test
    public void test1(){
        Collection coll = new ArrayList();

        //1.add():将元素e添加到coll中
        coll.add("AA");
        coll.add("BB");
        coll.add(123);//自动装箱
        coll.add(new Date());

        //2.size():获取添加的元素的个数
        System.out.println(coll.size());

        //3.addAll(Collection coll1):将coll1集合中的元素添加到当前的集合中
        Collection coll1 = new ArrayList();
        coll1.add(456);
        coll1.add("CC");
        coll.addAll(coll1);

        System.out.println(coll.size());
        System.out.println(coll);

        //4.clear():清空集合元素
        coll.clear();

        //5.isEmpty():判断当前集合是否有元素
        System.out.println(coll.isEmpty());
    }

    @Test
    //注意:向Collection接口的实现类的对象中添加数据obj时,要求所在类要重写equals()
    public void test2(){
       Collection coll = new ArrayList();
       coll.add(123);
       coll.add(456);
       coll.add(new String("Tom"));
       coll.add(false);
       coll.add(new Person("Jerry",21));

        //6.contains(Object obj):判断当前集合中是否包含obj
        boolean contains = coll.contains("Jerry");
        System.out.println(contains);

        //7.containsAll(Collection coll1):判断形参coll1中的所有元素是否都存在于当前集合中。
        Collection coll1 = Arrays.asList(132,456,789);
        System.out.println(coll.containsAll(coll1));

        //8.remove(Object obj):从当前集合中移除obj元素。
        System.out.println(coll.remove(123));

        //9.removeAll(Collection coll1):从当前集合中移除coll1中的所有元素,差集
        coll.removeAll(coll1);
        System.out.println(coll);
        System.out.println(coll1);

        //10.retainAll():求交集
        coll.retainAll(coll1);
        System.out.println(coll);

        //11.equals():有序的比较两个集合。全等返回true

        //12.hashCode():返回当前对象的哈希值

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

    @Test
    public void test3(){
        Collection coll = new ArrayList();
        coll.add(123);
        coll.add(456);
        coll.add(new String("Tom"));
        coll.add(false);
        coll.add(new Person("Jerry",21));

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

        //15.asList():数组 ---> 集合
        List<String> list = Arrays.asList(new String[]{"A", "C", "H", "M", "B"});//new 包装类的对象
        System.out.println(list);
    }
}

2.Iterator迭代器接口。遍历集合元素

●Iterator对 象称为迭代器(设计模式的一种),主要用于遍历Collection 集合中的元素。
GOF给迭代器模式的定义为:提供一种方法访问一个容器(container)对象中各个元素,而又不需暴露该对象的内部细节。迭代器模式,就是为容器而生。类似于“公交车.上的售票员”、“火车上的乘务员”、“空姐”。
●Collection接口继承了java.lang.lterable接口,该接口有一个iterator()方法,那么所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了Iterator接口的对象。
Iterator仅用于遍历集合,Iterator本身并不提供承装对象的能力。如果需要创建Iterator对象,则必须有一个被迭代的集合。
集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合的第一个元素之前。

package com.xudong.java;

import org.junit.Test;

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

public class IteratorTest {
    @Test
    public void test1(){
        Collection coll = new ArrayList();
        coll.add(123);
        coll.add(456);
        coll.add(new String("Tom"));
        coll.add(false);
        coll.add(new Person("Jerry",21));

        //遍历集合种元素
        Iterator iterator = coll.iterator();
        System.out.println(iterator.next());

        //方法一:不推荐
//        for (int i = 0; i < coll.size(); i++) {
//            System.out.println(iterator.next());
//        }

        //方法二:
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }

        //remove移除指定元素
        Iterator iter = coll.iterator();
        while (iter.hasNext()){
            Object obj = iter.next();
            if ("Tom".equals(obj)){
                iter.remove();
            }
        }

        System.out.println(coll);


    }
}

Iterator新特性

使用foreach循环遍历集合元素
●Java 5.0提供了foreach 循环迭代访问Collection和数组。
●遍历操作不需获取Collection或数组的长度,无需使用索引访问元素。
遍历集合的底层调用Iterator完成操作
●foreach还可以用来遍历数组。

package com.xudong.java;

import org.junit.Test;

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

public class forTest {
    @Test
    public void test(){
        Collection coll = new ArrayList();
        coll.add(123);
        coll.add(456);
        coll.add(new String("Tom"));
        coll.add(false);
        coll.add(new Person("Jerry",21));

        //for(集合元素类型 局部变量 : 集合对象)
        for (Object obj : coll){
            System.out.println(obj);
        }

    }

    @Test
    public void test1(){
        int[] arr = new int[]{1,2,33,2,5,4,6,7};
        //for(数组元素类型 局部变量 : 数组对象)
        for (int i : arr){ //将arr取到的值赋给i,数组内容不会改变。
            System.out.println(i);
        }
    }
}

3.List接口

3.1 List接口概述

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

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

3.2 ArrayList 源码分析

jdk 7情况下

ArrayList list = new ArrayList();//底层创建了长度是10的Object[]数组elementData

list.add(123);//eLementData[e] = new Integer(123);
...
list.add(11);//如果此次的添加导致底层eLementData数组容量不够,则扩容。

默认情况下,扩容为原来的容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中。
结论:建议开发中使用带参的构造器: ArrayList list = new ArrayList(int capacity)

jdk 8中ArrayList的变化:

ArrayList list = new ArrayList();//底层Object[] elementData初始化为{}.并没有创建长度为10的数组
list.add(123);//第一次调用add()时,底层才创建了长度10的数组,并将数据123添加到eLementData

后续的添加和扩容操作与jdk 7无异。

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

3.3 LinkedList 源码分析

Linkedlist list = new LinkedList(); //内部声明了Node类型的first和last属性,默认值为null
list. add(123);//将123封装到Node中,创建了Node对象。

3.4 List接口方法

●List除了从Collection集合继承的方法外,List 集合里添加了一些根据索引来操作集合元素的方法。

void add(int index, Object ele):在index位置插入ele元素
boolean addAll(int index, Collection eles):从index位置开始将eles中的所有元素添加进来
Object get(int index):获取指定index位置的元素
int indexOf(Object obj):返回obj在集合中首次出现的位置
int lastlndexOf(Object obj): 返回obj在当前集合中未次出现的位置
Object remove(int index):移除指定index位置的元素,并返回此元素
Object set(int index, Object ele):设置指定index位置的元素为ele
List subList(int fromIndex, int tolndex):返回从fromIndex到toIndex位置的子集合

4.Set接口

4.1 Set接口概述

●Set接口是Collection的子接口,set接口没有提供额外的方法
●Set集合是无序的,不可重复的

➢无序性:不等于随机性。存储的数据根据哈希值存储
➢不可重复性:保证添加的元素按照equals()判断时,不能返回true。

●Set判断两个对象是否相同不是使用==运算符,而是根据equals()方法

4.2 Set实现类之一:HashSet

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

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

HashSet集合判断两个元素相等的标准:两个对象通过hashCode()方法比较相等,并且两个对象的equals()方法返回值也相等。
●对于存放在Set容器中的对象,对应的类一定要重写equals()和hashCode(Object obj)方法,以实现对象相等规则。即:“ 相等的对象必须具有相等的散列码”

4.3 Set实现类之二:LinkedHashSet

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

4.4 Set实现类之三:TreeSet

●TreeSet是SortedSet接口的实现类,TreeSet 可以确保集合元素处于排序状态。
●TreeSet底层使用红黑树结构存储数据。可以按照添加对象的指定属性,进行排序。
●新增的方法如下:(了解)

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

●TreeSet两种排序方法:自然排序和定制排序。默认情况下,TreeSet采用自然排序。
●向TreeSet中添加的数据,要求是相同类的对象。

添加元素的过程,以HashSet为例:

我们向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a的哈希值,
此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置(即为:索引位置),判断数组此位置上是否已经有元素:
      如果此位置上没有其他元素,则元素a添加成功。---> 情况1
      如果此位置上有其他元素b(或以链表形式存在的多个元素),则比较元素a与元素b的hash值:
            如果hash值不相同,则元素a添加成功。---> 情况2
            如果hash值相同,进而需要调用元素a所在类的equlas()方法:
                  equals()返回true,元素a添加失败
                  equaLs()返回false,则元素a添加成功。--->情况3

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

Map接口

1.Map的实现类

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

Map结构的理解:

Map中的key:无序的、不可重复的,使用Set存储所有的key ---> key所在的类要重写 equals() 和 HashCode() (HashMap为例)
Map中的value:无序的、可重复的,使用Collection存储所有的value。---> value 所在的类要重写 equals()
一个键值对: key-value 构成了一个Entry对象。
Map中的entry:无序的、不可重复的,使用Set存储所有的entry

2.Map接口常用的方法:

添加、删除、修改操作:

➢Object put(Object key,Object value):将指定key-value添加到(或修改)当前map对象中
➢void putAI(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集合

3.HashMap的底层实现原理

➢JDK7.0
HashMap map = new HashMap():
在实例化以后,底层创建了长度是16的一维数组Entry[] table.
...可能已经执行过多i次put...
map.put (key1, value1):
首先,调用key1所在类的hashCode()计算key1哈希值,此哈希值经过某种算法计算以后,得到在Entry 数组中的存放位置。如果此位置上的数据为空,此时的key1-value1 添加成功。---- 情况1
●如果此位置上的数据不为空,( 意味着此位置上存在一个或多个数据(以链表形式存在)),比较key1和已经存在的一个或多个数据的哈希值:
      ●如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1 添加成功。---- 情况2
      ●如果key1的哈希值和已经存在的某一个数据(key2-value2) 的哈希值相同,继续比较:调用key1所在类的equals(key2)
            ●如果equals()返回false:此时key1-value1添加成功。---- 情况3
            ●如果equaLs()返回true:使用value1替换value2。

      补充:关于情况2和情况3:此时key1-value1 和原来的数据以链表的方式存储。
      在不断添加的过程中,会涉及到扩容问题,当超出临界值(且要存放的位置非空)时,扩容。
      默认的扩容方式:扩容为原来容量的2倍,并将原有的数据复制过来。

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

4.HashMap源码中的重要常量

**DEFAULT_INITIAL_CAPACITY **: HashMap的默认容量,16
**MAXIMUM_CAPACITY **: HashMap的最大支持容量, 2^30
DEFAULT_LOAD_FACTOR: HashMap的默认加载因子0.75
TREEIFY_THRESHOLD: Bucket中链表长度大于该默认值8,转化为红黑树
UNTREEIFY_THRESHOLD: Bucket中红黑树存储的Node小于该默认值,转化为链表MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量64。(当桶中Node的数量大到需要变红黑树时,若hash表容量小于MIN_TREEIFY_CAPACITY时,此时应执行) resize扩容操作这个MIN_TREEIFY_CAPACITY的值至少是TREEIFY_THRESHOLD的4倍。)
table:存储元素的数组,总是2的n次幂
entrySet:存储具体元素的集
size: HashMap中存 储的键值对的数量
modCount: HashMap扩容和结构改变的次数。
threshold:扩容的临界值12,=容量填充因子 (160.75)= 12
loadFactor:填充因子

5.LinkedHashMap底层原理

6.Properties

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

五、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处元素进行交换

1.Collections常用方法

查找、替换

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中

List dest = Array.asList(new Object[src.size()]);
Sysout.out.println(dest.size);//src.size()
Collections.copy(dest,src);
Sysout.out.println(dest);

boolean replaceAll(List list, Object oldVal, Object newVal):使用新值替换List对象的所有旧值

同步控制

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

以上是关于Java-集合的主要内容,如果未能解决你的问题,请参考以下文章

201621123062《java程序设计》第九周作业总结

Java方法

金蝶handler中 collection 代码片段理解

Alfred常见使用

比较 C# 中的字符串片段并从集合中删除项目

带有红宝石集合/可枚举的酷技巧和富有表现力的片段[关闭]