Java10 集合

集合

集合接口等级:

分类
在这里插入图片描述
在这里插入图片描述

(1):Collection接口:单例集合接口

(2):map接口:双链接口

Collection:单例集合接口,将数据一个一个存储,存储的是值。

(1)特点:Collection接口为单例集合接口,将数据一个一个存储,存储的是值。

(2)子接口:分为list接口,set接口和queue接口

2.list接口:集合中最基础的接口,行为和数组一致

《1》特点:

​ 1.元素是有序的,即存储和取出的顺序一致

​ 2元素存储是可以重复的

《2》实现类:

1.ArrayList类

2.LinkedList类

3.Vetor类

4.Stack类
在这里插入图片描述

ArrayList类:泛型集合

《1》对象创建:

ArrayList<泛型> list =new ArrayList<泛型>()

《2》泛型于jdk1.5添加,可帮助我们建立类型安全的集合,再进行调用时必须传入实际的类型,对于泛型我们可以简单的理解为数据类型的占位符(必须是引用数据类型),它的优点:提高代码安全性,增强代码的可读性。

《3》方法:以对象list为例

(1)加:

(1.1)list.add(元素)—添加元素到集合list末尾,返回值为boolean,添加成功则为true。

(1.2)list.add(int index,元素)—添加元素到集合list指定下标处,无返回值。

(1.3)list.addAll(collection c)添加单例集合c到list集合末尾,返回值为boolean,添加成功则为true。

(1.4)list.addAll(int index,collection c)添加单例集合c到list集合的指定下标处,返回值为boolean,添加成功则为true

(2)查:

(2.1)list.get(int index)获取指定下标元素,返回值为泛型

(2.2)iist.size()获取集合长度,返回值为int型

(2.3)list.indexOf(元素),查找元素下标,返回值为int型,如果不存在,返回-1.

(2.4)list.contains(元素)判断集合中是否包含此元素,返回值为boolean

(2.5)list.isEmpty()判断集合是否为空,返回值为boolean

(2.6)list.equals(list2)判断两个集合中元素是否相同,返回值为boolean

(2.7)遍历集合:

​ (2.7.1)for循环遍历

​ (2.7.2)foreach循环遍历

​ (2.7.3)获取普通迭代器遍历:

Iteractor<泛型> it=list.iteractor();
Whlie(it.hasNext()){ 判断有无下一个元素
	It.next  //获取下一个元素
}

(2.7.4)获取list迭代器遍历:

Iistiteractor<泛型> it=list.listiteractor();
//可传入开始遍历的下标,也可逆序遍历
Whlie(it.hasNext()){ //判断有无下一个元素
	It.next()  //获取下一个元素
}

Whlie(it.hasPrevious()){ //判断有无上一个元素
	It.Previous()  //获取上一个元素
}

(3) 改:list.set(int index ,元素)修改集合中指定下标的元素为修改后的元素

(4) 删:

(4.1)list.remove(int index)删除集合中指定下标的元素,返回值为删除的元素

(4.2)list.remove(Object o)删除指定元素,返回值为boolean,注意如果参数传入数字,则系统默认该数字为集合下标,如果要删除的元素为该数字,则要强调传入的参数为Integer型

(4.3)list.clear()清空集合的所有元素,注意集合仍然存在,但长度为0

(4.4)list.removeAll(collection c)删除集合list在集合c中相同的元素,即求集合list和集合c的差集,返回值为boolean

(4.5)list.retainAll(collection c)保留集合list在集合c中相同的元素,即求集合list和集合c的交集,返回值为boolean

(5)其他:

(5.1)list.subList(int formindex,int toindex)截取集合中从index下标到toindex下标(不包括toindex)的部分,返回值和list集合一致。

(5.2)list.clone()克隆集合list,返回值与list一致

(5.3)list.sort(定义规则)对集合进行排序,需要自定义排序规则,创建匿名内部类实现comparator接口,重写compare方法。

(5.4)list.toArray()将集合转成数组,返回值为Object型数组

(5.5)list.toArray(泛型[] a)将集合转成数组,返回值为泛型数组

《4》ArrayList的其他两种构造方法:

ArrayList<泛型> list =new ArrayList<泛型>int x)自定义ArrayList底层数组的初始化长度

ArrayList<泛型> list =new ArrayList<泛型>(collection c)按照传入的集合c对Arraylist底层存储的数组的长度进行初始化

《5》Arraylist存储的扩容原理:Arraylist集合的底层存储方式为Object型的数组,采用无参构造创建对象时,开始object型数组长度初始化为0,当第一次添加元素时,数组长度初始化为10,当存储到第十一个元素时,开始进行扩容,扩容原理为原来数组长度的1.5倍。

Linkedlist集合:

双向链表,节点为node类型

E item
Node<E> next
Node<E> prev
使用场景:大量的删除和插入元素

尾插ArrayList和LinkedList时间复杂度为O(1)
中间插入ArrayList时间复杂度O(n),LinkedList时间复杂度为O(1)

《1》由于Linkedlist和Arraylist都是list接口的实现类,因此Linkedlist可以使用Arraylist的绝大部分方法。

《2》Linkedlist独有的方法:以对象list为例

(1)list.addFirst(元素)添加元素到集合第一位

(2)List.addLast()添加元素到集合最后一位

(3)list.getFirst()获取集合首位元素

(4)list.getLast()获取集合最后一个元素

(5)list.removeFirst()删除集合首位元素

(6)list.removeLast()删除集合最后一个元素

《3》遍历集合时不推荐使用for循环,其余三种方式可使用

《4》Linkedlist底层存储方式为双指向链表,因此不存在扩容问题

Vector集合:

《1》特点:基于Object[]数组 elementData来实现的,类似于Arraylist

《2》创建对象的三种方式:

(1)Vetor<泛型> v1=new Vetor<泛型>()默认数组长度初始化为10,扩容时按2倍扩容

(2)Vetor<泛型> v1=new Vetor<泛型>(int x)自定义数组初始化长度为x

(3)Vetor<泛型> v1=new Vetor<泛型>(int x,int y)自定义数组初始化长度为x,扩容值为x+y

《3》Vetor与Arraylist的对比:

(1)线程:Arraylist线程不安全,Vetor线程安全

(2)效率:Arraylist效率更高,Vetor效率相对较低

(3)初始化:Arraylist开始初始化为0,第一次添加元素时,初始化为10。Vetor开始初始化为10.

(4)扩容:Arraylist按之前的1.5倍扩容,Vetor按2倍或自定义扩容值进行扩容

Stack集合:Vetor的子类

《1》特点:先进后出FILO,即最先存入的元素最后被拿出

《2》方法:以s1为例:

(1)s1.push(元素)添加元素------入栈

(2)S1.pop(元素)获取栈顶元素,并弹出该元素(出栈)

(3)S1.peek(元素)获取栈顶元素,不弹出该元素(出栈)

Set接口:存储是无序的,且集合中的元素不可重复

《1》特点:存储是无序的,且集合中的元素不可重复

《2》实现类:

(1)hashset

(2)Linkedhashset

(3)Treeset
在这里插入图片描述

Hashset集合:

《1》创建对象:

(1)Hashset<泛型> hs=new Hashset<泛型>()

(2)Hashset<泛型> hs=new Hashset<泛型>(collection c)

《2》方法:以对象hs为例

(1)hs.add(元素)添加元素到集合末尾,返回值为boolean

如果添加重复元素,则返回false,即无法添加成功

(2)hs.addAll(collection c)添加一个集合

(3)Hs.contains(元素)判断元素是否存在

(4)Hs.clear()清空集合内的所有元素

(5)遍历集合:for each循环,(普通)迭代器

《3》用途:hashset集合可以帮我们去重

《4》hashset集合去重的原理:

《4.1》通过hashcode判断两个元素是否有相同的hashcode值,如果有,则进行下一步判断,如果没有则直接存放

《4.2》通过equals方法比较两个元素内容是否相同,如果相同则不进行存放,反之则存放。

注意:重写hashcode方法时,必须要同时重写equals方法,因为可能有hashcode冲突的现象,即不同的对象可能存在相同的hashcode值。

Linkedhashset:是有序不重复的set集合,继承于hashset

Treeset:排序,去重的集合

(1)默认排序规则:《1》Integer类默认升序排列

​ 《2》String类默认使用字符+长度进行排序

(3)注意事项: 《1》泛型必须要是引用数据类型

​ 《2》泛型如果是自定义类型,必须要重写hashcode放法和equals方法同时必须要实现comparable接口,且重写其中的compareto方法,定义排序规则,或者使用有参构造传入一个comparactor对象,即匿名内部类重写compare方法定义排序,注意:如果同时出现实现comparable接口和给定comparactor对象(排序规则),后者的优先级要大于前者,前者为类级别,后者为对象级别

(4)构造方法:《1》无参构造

​ 《2》有参构造:即传入一个conparactor对象,定义排序规则,优先级高于类上定义的comparable接口,因为此处是对此对象特殊强调的比较规则

Map接口:键值对集合(可类比数学中的函数)

1.HashMap集合:map接口的实现类

(1)特点:《1》元素按一对一对进行存取(key-value)

​ 《2》无序性

​ 《3》key唯一,不可重复,value可重复

​ 《4》key和value均可为null

(2)方法:以对象hsm为例

《1》添:《1.1》hsm.put(key,value)返回值为oldvalue,存放时如果key不存在,则返回null,并添加新的键值对,如果key值已存在,则返回原key对应的值,并覆盖掉原key对应的值

《1.2》hsm.putifabsent(key,value)返回值为oldvalue,存放时如果key不存在,则返回null,并添加新的键值对,如果key值已存在,则返回原key对应的值,不对原key对应的值做任何处理

《1.3》hsm.putAll(map m)将指定集合m中的键值对全部存入hsm集合中,如果原集合中已存在此key,则覆盖原key对应的value。此方法无返回值

​ 《2》查:《2.1》hsm.get(key)通过key获取对应的value,如果key不存在,则返回null,返回值为value

​ 《2.2》hsm.getOrDefault(key,default)通过key获取对应的value,如果key不存在,则返回default,返回值为value

​ 《2.3》hsm.containskey(key),判断key是否存在,返回值为boolean

​ 《2.4》hsm.containsvalue(value),判断value是否存在,返回值为boolean

​ 《2.5》hsm.isEmpty(),判断集合是否为空,返回值为boolean

​ 《2.6》hsm.size(),获取集合中键值对的个数,即集合的长度,返回值为int

​ 《3》改:《3.1》hsm.replace(key,value),根据key,替换对应的value,返回原value。如果key不存在,返回null,返回值为oldvalue。

​ 《3.2》 hsm.replace(key,oldvalue,newvalue)根据key和对应的value,替换value,只有key和value都对应上才能替换成功,返回值为boolean

​ 《4》删: 《4.1》hsm.remove(key)通过key删除对应的键值对,返回key对应的value,如果key不存在,则返回null

​ 《4.2》 hsm.replace(key,value)根据key和对应的value,删除对应的键值对,只有key和value都对应上才能删除成功,返回值为boolean

​ 《4.3》hsm.clear()清空集合中的键值对,

​ 《5》遍历:《5.1》hsm.keyset()获取集合中所有的key,返回值为set类集合。遍历所有的key,再通过hsm.get(key)可获取到所有key对应的value

public class Main {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        map.put("apple", 123);
        map.put("pear", 456);
        map.put("banana", 789);
        
        for (String key : map.keySet()) {
            Integer value = map.get(key); // 根据key,获取value
            System.out.println(key + " = " + value);
        }
    }
}

​ 《5.2》hsm.values()直接获取所有的value,返回值为collection集合

Map<String, Integer> map = new HashMap<>();
map.put("apple", 123);
map.put("pear", 456);
map.put("banana", 789);

Collection<Integer> values = map.values();
for (Integer val : values) {
    System.out.println("value值= " + val);
}

​ 《5.3》hsm.entryset()获取所有的键值对。每个key-value键值对都通过Entry类型的对象封装(可理解为每个key-value都成为了Entry类型对象的成员变量),通过对象名.getkey和对象名.getvalue,获取所有的键值对

public class Main {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        map.put("apple", 123);
        map.put("pear", 456);
        map.put("banana", 789);
        
        for (Map.Entry<String, Integer> entry : map.entrySet()) {
            String key = entry.getKey();
            Integer value = entry.getValue();
            System.out.println(key + " = " + value);
        }
    }
}

《5.4》for-each进行遍历

Map<String, Integer> map = new HashMap<>();
map.put("apple", 123);
map.put("pear", 456);
map.put("banana", 789);

map.forEach(new BiConsumer<String, Integer>() {
    @Override
    public void accept(String key, Integer value) {
         System.out.println(key + " = " + value);
    }
});

HashMap集合put元素时的底层原理:

(1)使用hash计算元素存储到数组的位置i=(数组长度-1)%hash,如果此处没有位置,则直接存储

(2)如果此处有元素,判断key地址或内容是否相同,如果是则覆盖原元素中的value值

(3)如果key不同则说明发生了hash冲突,此时元素已node节点形式连接到原位置元素的后面

先判断是否为树形,如果是,则按照树形结构添加,

如果不是则按照单向链表添加,已p==null作为判断,将新增元素添加到链表末尾

(4)扩容问题:

img

​ 《1》首次添加元素,数组长度0—16,扩容阈值0—12.

​ 《2》链表长度>8,数组长度<64,数组长度和扩容阈值都按两倍扩容

​ 《3》数组中元素大于扩容阈值,扩容机制同上

总结

最常用的一种Map实现是HashMap;(无序,key唯一)
●HashMap的数据结构采用数组+链表+红黑树
●HashMap的按照key的hash值计算数组中的存储位置下标,计算方式:(n-1)&hash。
●如果在该下标位置已经存在元素,代表产生哈希冲突,则采用链地址法处理,以单向链表的形式,将新元素存储在链表的尾部(尾插法)。
●当链表中Node节点的数量大于8并且数组的长度大于64时,链表会转换成一个红黑树,有利于查找搜索。
●HashMap的默认容量为16,加载因子为0.75f,当集合元素个数超过扩容阈值(容量*加载因子)时,HashMap会将底层数组容量按照2倍进行扩容。

2.LinkedHashMap集合:强调有序且去重,底层多维护了一条双向链表,用于存储添加元素顺序的信息

3.Treemap集合:可排序的键值对集合,以key类型的默认排序规则排序,如果key为自定义类型,则必须要实现comparable接口定义排序规则,或者采用treemap的有参构造,传入排序规则参数(comparactor)

4. Hashtable接口:无序,key唯一,key和value不可为null,线程更加安全,无参构造初始化对象时,数组长度有默认值

Collections集合算法工具类的一些方法:

在这里插入图片描述

1public static boolean disjoint(Collection<?> c1, Collection<?> c2)判断两个集合中是否存在相同元素,如果存在,返回false,不存在返回true2public static <T> void fill(List<? super T> list, T obj)将集合中的所有元素进行填充(修改),如果集合中没有元素,则不填充

(3public static int frequency(Collection<?> c, Object o)获取集合中指定元素的出现次数,返回值为int4public static T max(Collection<? extends T> coll)获取集合中的最大值,如果没有特别强调比较规则,则按照泛型默认的比较规则给出最大值

(5public static T min(Collection<? extends T> coll)获取集合中的最小值,如果没有特别强调比较规则,则按照泛型默认的比较规则给出最小值

(6public static void sort(List<T> list)对集合进行排序,如果没有特别强调排序规则,则按照泛型的默认排序规则进行排序

(7public static void reverse(List<?> list)将集合中的元素逆序(反转)

(8public static void shuffle(List<?> list)将集合中的元素打乱

(9public static void swap(List<?> list, int i, int j)将集合中两个位置的元素进行交换

(10public static int binarySearch(List<?> list, T key) 二分查找指定元素下标,要求集合必须有序(排序)

面试问到的:

Collection和Collections的区别

Collection 是一个接口,定义了所有集合类的通用行为和方法。
Collections 是一个工具类,包含了对集合进行操作的静态方法,如排序、查找等。

Collection:
Collection 是 Java 中表示一组对象的接口。它是所有集合类的根接口,定义了集合框架的通用行为和方法。Collection 接口继承自 Iterable 接口,提供了对集合中元素进行基本操作的方法,添加、查找、修改、删除、遍历
Collection 接口的子接口包括 List、Set 和 Queue。

Collections:
Collections 是 Java 中一个实用类,位于 java.util 包中。它包含了一些静态方法,用来操作各种集合类,比如 List、Set 和 Map
Collections 类提供了对集合进行排序、搜索、线程安全化等操作的静态方法。例如,sort() 方法用于对列表进行排序,synchronizedCollection() 方法用于返回一个线程安全的集合。

ArrayList和LinkedList的区别

①数据结构:ArrayList为数组,随着元素的增加而动态扩容。
LinkedList为双向链表,随着元素的增加,创建新的Node节点并进行分配空间。
②扩容方式:
ArrayList无参构造初始化为0,第一次添加元素时,扩容的大小为10。如果容量不足,则按原容量1.5倍扩容进行增长。
LinkedList由于采用链表结构,每次添加元素,都在创建新的Node节点进行分配空间,所以不存在扩容
③使用场景
ArrayList类:适用于数据连续的遍历,读多写少的场景。
查询效率高,增加和删除效率较低,需要大量移动元素。
LinkedList类:适合数据频繁的添加和删除操作,写多读少的场景。
插入、删除元素效率高,遍历和随机访问效率低下。
④安全性
ArrayList 和 LinkedList 都不是线程安全的。

HashMap扩容机制

在这里插入图片描述

扩容问题:
1.首次无参初始化添加元素进行扩容,resize()—16
2.链表>=8 并且数组长度<64 扩容—2倍扩容
3.存放的元素已经达到扩容阈值(加载因子*数组长度) — 2倍扩容
在这里插入图片描述

HashMap的put过程

image20210828210330896

HashMap put过程
初始化

HashMap有4个构造器,其他构造器如果用户没有传入initialCapacity 和loadFactor这两个参数,会使用默认值一般如果new HashMap() 不传值,默认大小是16,负载因子是0.75, 如果自己传入初始大小k,初始化大小为 大于k的 2的整数次方,例如如果传10,大小为16。

put()过程

判断数组是否为空,为空进行初始化;
不为空,计算 key的 hash 值,通过(n - 1) & hash(记不住就直接说哈希算法)计算应当存放在数组中的下标 index;查看 table[index] 是否存在数据,没有数据就构造一个Node节点存放在 table[index] 中;存在数据,说明发生了hash冲突(存在两个节点key的hash值一样), 继续判断key是否相等,相等,用新的value替换原数据(onlyIfAbsent为false);
如果不相等,判断当前节点类型是不是树型节点,如果是树型节点,创造树型节点插入红黑树中;(如果当前节点是树型节点证明当前已经是红黑树了)
如果不是树型节点,创建普通Node加入链表中;判断链表长度是否大于 8并且数组长度大于64, 大于的话链表转换为红黑树;

插入完成之后判断当前节点数是否大于阈值,如果大于开始扩容为原数组的2倍。

HashMap的底层原理分析

  1. 关键成员变量/常量:

(1)Node<k,v>[] table:HashMap底层存储数组

(2)Float loadfactor :加载因子,无参初始化时为默认加载因子

(3)Int threshold:扩容阈值,一般为table数组的长度*加载因子(0.75)

(4)Int Size:集合的长度(kv键值对的个数)

(5) final int DEFAULT_INITIAL_CAPACITY = 1 << 4; 默认数组长度16

(6)final int MAXIMUM_CAPACITY* = 1 << 30;最大数组长度

(7)final float DEFAULT_LOAD_FACTOR = 0.75f;默认加载因子

(8)final int TREEIFY_THRESHOLD = 8;转树形时链表的最小长度

(9)final int MIN_TREEIFY_CAPACITY = 64;转树形时数组的最小长度

  1. put元素时底层原理:

(1)通过扰动函数计算所存放元素中key的hash值

在这里插入图片描述
在这里插入图片描述
(2)添加元素前判断之前的数组table是否为空,如果是,则进行扩容
在这里插入图片描述

具体扩容原理如下:
在这里插入图片描述

扩容后:table数组长度由开始的0,变成16,扩容阈值从0变成了12

(3)通过table数组长度-1&hash的方法,确定要添加元素所存放的位置,并判断此位置是否已有元素,如果没有则可直接存放
在这里插入图片描述

(4)如果此位置已经有元素,通过hash和equals判断要添加元素的key值和已经在此位置元素的key值是否相同,如果key值不同,则对原位置元素的value进行覆盖
在这里插入图片描述

(5)如果key值相同,则说明发生了hash冲突,此时将要添加的元素以node节点的形式连到原位置元素的后面,先判断是否为树形节点,如果不是则按单向链表的形式进行连接
在这里插入图片描述
(6)后续添加元素,如果仍然在此位置发生哈希冲突,就继续往最后添加的元素后面连接,用p.next==null判断,在此过程中重复(4)的过此,即判断后续添加的元素与前面的元素的key值是否相同,相同则覆盖原元素的value,不相同则进行连接

(7)如果同一个位置的连接的元素超过8个,同时table数组的长度小于64,则需进行扩容,当同一个位置的连接的元素超过8个同时数组长度超过64,则改为红黑树的形式连接要添加的元素
在这里插入图片描述
在这里插入图片描述

此时的扩容机制为数组长度按两倍扩容,扩容阈值也按两倍扩容
在这里插入图片描述

(8)还有一种情况也需要扩容,即数组中的元素个数大于扩容阈值,此时会进行扩容
在这里插入图片描述
此时的扩容机制和上面相同,即数组长度和扩容阈值都按两倍扩容!
总结:

1.使用hash计算元素存储到数组的位置i =  (n-1)&hash,如果此位置没有元素,直接存储
2.如果当前的存储位置有元素,并且key地址相等,或者key的内容相同--》key同,则覆盖value
3.如果当前存储的位置有元素,并且key不相同,产生hash冲突
  way1:判断是否为树形节点,如果是树形节点,采用树形创建方式,将元素进行添加
  way2:不是树形节点,则是链表: p.next == null 放新的元素(判断每个节点是否和新增的节点的key是否相同)
		  链表>=8 并且数组长度<64  扩容
 		  链表>8 并且数组长度>64  转树形节点

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值