JavaEE笔记——集合

目录

 

类集

链表

原理图

链表和二叉树伪代码结构

队列

红黑树

Collection接口

List接口

ArrayList常用方法

Vector

LinkedList常用方法

Iterator与ListIterator

foreach

Set

Map

HashMap

HashMap实现原理

Java中Map常用方法总结以及遍历方式的汇总

Map三大常用功能方法表格整理

HashMap/Hashtable/ConcurrentHashMap/TreeMap/LinkedMap之间的区别

哈希表的初始容量和散列因子

存储自定义对象

JDK 9 集合新特性


类集

类集是什么:Java用于实现数据结构的库,在java.util包中。

链表

定义:链表 [Linked List]:链表是由一组不必相连(不必相连:可以连续也可以不连续)的内存结构(节点),按特定的顺序链接在一起的抽象数据类型。

原理图

链表和二叉树伪代码结构

队列

红黑树

Collection接口

Collection 接口是在整个 Java 类集中保存单值的最大操作父接口,里面每次操作的时候都只能保存一个对象的数据。 此接口定义在 java.util 包中。

本接口中一共定义了 15 个方法,那么此接口的全部子类或子接口就将全部继承以上接口中的方法。

但是,在开发中不会直接使用 Collection 接口。而使用其操作的子接口:List、Set。

List接口

在整个集合中 List 是 Collection 的子接口,里面的所有内容都是允许重复的。

此接口的实现类,常用的实现类有如下几个:

ArrayList(95%)、Vector(4%)、LinkedList(1%)

  • Array和Vetor采用的是动态数组来存储,LinkList采用的是双向链表来实现,可以模拟栈和队列。

ArrayList常用方法

 

下面是总结了一些比较常用的ArrayList类成员方法:

  • 增加元素到链表中

    • boolean add(Element e) 增加指定元素到链表尾部.

    • void add(int index, Element e) 增加指定元素到链表指定位置.

  • 从链表中删除元素

    • void clear() 从链表中删除所有元素.

    • E remove(int index) 删除链表中指定位置的元素.

    • protected void removeRange(int start, int end) 删除链表中从某一个位置开始到某一个位置结束的元素。

  • 获取链表中的元素

    • E get(int index) 获取链表中指定位置处的元素.

    • Object[] toArray() 获取一个数组,数组中所有元素是链表中的元素.(即将链表转换为一个数组)

  • 修改某个元素

    • E set(int index, E element) 将链表中指定位置上的元素替换成新元素。

  • 搜索元素

    • boolean contains(Object o) 如果链表包含指定元素,返回true.

    • int indexOf(Object o) 返回元素在链表中第一次出现的位置,如果返回-1,表示链表中没有这个元素。

    • int lastIndexOf(Object o) 返回元素在链表中最后一次出现的位置,如果返回-1,表示链表中没有这个元素。

  • 检查链表是否为空

    • boolean isEmpty() 返回true表示链表中没有任何元素.

  • 获取链表大小

    • int size() 返回链表长度(链表包含元素的个数).

以上是ArrayList类中使用比较多的成员方法。

Vector

方法与ArrayList基本相同

LinkedList常用方法

集合 的体系: ------------| Collection 单例集合的根接口 ----------------| List 如果是实现了List接口的集合类,具备的特点: 有序,可重复。 -------------------| ArrayList ArrayList 底层是维护了一个Object数组实现的。 特点: 查询速度快,增删慢。 -------------------| LinkedList LinkedList 底层是使用了链表数据结构实现的, 特点: 查询速度慢,增删快。 -------------------| Vector(了解即可) 底层也是维护了一个Object的数组实现的,实现与ArrayList是一样的,但是Vector是线程安全的,操作效率低。

----------------| Set 如果是实现了Set接口的集合类,具备的特点: 无序,不可重复。 -------------------| HashSet 底层是使用了哈希表来支持的,特点: 存取速度快. -------------------| TreeSet 如果元素具备自然顺序 的特性,那么就按照元素自然顺序的特性进行排序存储。

Linkedlist特有的方法: 1:方法介绍 addFirst(E e) addLast(E e)

getFirst() getLast()

removeFirst() removeLast()

2:数据结构 1:栈 (1.6) : 主要是用于实现堆栈数据结构的存储方式。 先进后出 push() pop() 2:队列(双端队列1.5): 主要是为了让你们可以使用LinkedList模拟队列数据结构的存储方式。 先进先出 offer() poll()

3:返回逆序的迭代器对象

descendingIterator() 返回逆序的迭代器对象

Iterator与ListIterator

       //均用于取数据
        //Iterator:适用于List和set
        //ListIterator:只适用于List
        ArrayList<Integer> data = new ArrayList<>();
        data.add(1);
        data.add(2);
        data.add(3);
        data.add(4);
        data.add(5);
​
//        Iterator<Integer> iterator = data.iterator();
//        //迭代器使用的是指针
//        //iterator.hasNext()是将指针指向下一个位置,看是否存在数据,返回的是布尔值
//        /*
//        while (iterator.hasNext()){
//            Integer i =iterator.next();//iterator.next():将指针往下移动一个位置
//            System.out.println(i);
//        }
//
//         iterator.next();
//        //在remove前一定要用next()方法取到位置,因为只有这样把指针移动到有值得地方才能删除
//         iterator.remove();
//        System.out.println(data.size());
​
        ListIterator<Integer> iterator = data.listIterator();
        iterator.add(100); //添加是在索引0的前面添加,索引下面指针移动需要3次
        iterator.next();//指针指向索引0
        iterator.next();//指针指向索引1
        iterator.set(200);
        System.out.println(data.size());
​
        iterator.previous();//指针从下往上移动一位
        iterator.previous();
        iterator.previous();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }

foreach

  • foreach:增强For循环,最早出现在C#中

  • 用于迭代数组 或 集合(Collection接口下的类)

  • 语法:for(数据类型 变量名:集合或数组名){}

    for (String s:set){
        System.out.println(s);
    }

Set

  • 不包含重复元素的集合。 更正式地说,集合不包含元素对e1e2 ,使得e1.equals(e2)和最多一个null元素。 正如其名称所暗示的,此接口模拟数学抽象。

  • 注意:如果将可变对象用作set元素,则必须非常小心。 如果在对象是集合中的元素时以影响equals比较的方式更改对象的值,则不指定集合的行为。

HashSet

储存数据的方式:以散列表的形式进行无序存放。

HashSet<String> set = new HashSet<>();
boolean flag1 =set.add("锄禾日当午");
set.add("汗滴禾下土");
set.add("谁知盘中餐");
set.add("粒粒皆辛苦");
boolean flag2 =set.add("锄禾日当午");
System.out.println(flag1);
System.out.println(flag2);//存入重复元素,返回false
Iterator<String> iterator = set.iterator();
for (String s:set){
    //因为哈希表也是散列表,数据在散列表中的存储顺序不能确定,所以打印出来的
    System.out.println(s);
}
//打印set里面的内容
/*while (iterator.hasNext()){
    System.out.println(iterator.next());
}*/

TreeSet

  • 储存数据的方式:以二叉树的形式进行有序存放。

  • 排序是根据字符的ASCII码进行排序

TreeSet<String> data = new TreeSet<>();
data.add("b");
data.add("c");
data.add("a");
data.add("d");
for (String s:data){
    System.out.println(s);
}
//System.out.println((char)1); //打印ASCII的方法

Map

  • collection是单值储存集合,map是双值储存集合(键值对)。

  • Map是将键映射到值的对象。 不能包含重复的键; 每个键最多可以映射一个值。

  • map集合的键(key)不可重复。大多数Set集合是用了map的键来存储数据,来实现数据的不可重复性。

HashMap

  • 哈希表(hashmap),又称为散列表,是以对象数组+链表的形式进行储存。即把数组看作二维数组,在数组的某个索引中插入链表进行存储。其中,数组的索引我们又称为哈希桶。

  • 如何得到哈希表?通过哈希码(hashcode)对数组长度取余,得到的余数就是数据存放的哈希桶编号。当有两个以上的数据放在同一个哈希桶中,那么就会使用链表的形式对这两个数据进行储存。

    • 当哈希桶中的数据量大于8时,从链表转换为红黑二叉树

    • 当哈希值中的数据量减少到6时,从红黑二叉树转换为链表

    • 当哈希桶中的数据量等于7时,那么请问当数据量减少到6时,哈希表需要转换为链表吗?

      • 我们确定哈希桶数据量为7时,该形式是二叉树还是链表。如果数据量为7时,还是链表,又怎么用得着转为链表呢

  • 使用默认初始容量(16)和默认加载因子(0.75)构造一个空 HashMap

  • 散列因子的作用:当散列因子达到某阈值时,对象数组将进行动态扩容。比如下面这个初始桶数量为16,散列因子(加载因子)0.75,当数组的桶容量达到75%时,数组容量将扩大一倍,桶数量达到32.

HashMap实现原理

Java中Map常用方法总结以及遍历方式的汇总

一、整理:

看到array,就要想到角标。

看到link,就要想到first,last。

看到hash,就要想到hashCode,equals.

看到tree,就要想到两个接口。Comparable,Comparator。

 

二、Map与Collection在集合框架中属并列存在

1.Map存储的是键值对

2.Map存储元素使用put方法,Collection使用add方法

3.Map集合没有直接取出元素的方法,而是先转成Set集合,在通过迭代获取元素

4.Map集合中键要保证唯一性

也就是Collection是单列集合, Map 是双列集合。

总结:

Map一次存一对元素, Collection 一次存一个。Map 的键不能重复,保证唯一。

Map 一次存入一对元素,是以键值对的形式存在.键与值存在映射关系.一定要保证键的唯一性.

三、Map中常见方法:

1、添加:

1、V put(K key, V value) (可以相同的key值,但是添加的value值会覆

盖前面的,返回值是前一个,如果没有就返回null)

2、putAll(Map<? extends K,? extends V> m) 从指定映射中将所有映射关

系复制到此映射中(可选操作)。

2、删除

1、remove() 删除关联对象,指定key对象

2、clear() 清空集合对象

3、获取

1:value get(key); 可以用于判断键是否存在的情况。当指定的键不存在的时候,返

回的是null。

3、判断:

1、boolean isEmpty() 长度为0返回true否则false

2、boolean containsKey(Object key) 判断集合中是否包含指定的key

3、boolean containsValue(Object value) 判断集合中是否包含指定的value

4、长度:

Int size()

四、遍历Map的方式:

1、将map 集合中所有的键取出存入set集合。

Set<K> keySet() 返回所有的key对象的Set集合,再通过get方法获取键对应的值。

2、 values() ,获取所有的值.

Collection<V> values()不能获取到key对象

3、 Map.Entry对象 推荐使用 重点

Set<Map.Entry<k,v>> entrySet() 将map 集合中的键值映射关系打包成一个对象。

Map.Entry对象通过Map.Entry 对象的getKey,getValue获取其键和值。

第一种方式:使用keySet

将Map转成Set集合(keySet()),通过Set的迭代器取出Set集合中的每一个元素(Iterator)就是Map集合中的所有的键,再通过get方法获取键对应的值。

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
​
public class Demo1 {
    public static void main(String[] args) {
        Map<Integer, String> map = new HashMap<Integer, String>();
        map.put(1, "aaaa");
        map.put(2, "bbbb");
        map.put(3, "cccc");
        System.out.println(map);
​
        //
        // 获取方法:
        // 第一种方式: 使用keySet
        // 需要分别获取key和value,没有面向对象的思想
        // Set<K> keySet() 返回所有的key对象的Set集合
​
        Set<Integer> ks = map.keySet();
        Iterator<Integer> it = ks.iterator();
        while (it.hasNext()) {
            Integer key = it.next();
            String value = map.get(key);
            System.out.println("key=" + key + " value=" + value);
        }
    }
}

第二种方式: 通过values 获取所有值,不能获取到key对象

 1 public static void main(String[] args) {
 2         Map<Integer, String> map = new HashMap<Integer, String>();
 3         map.put(1, "aaaa");
 4         map.put(2, "bbbb");
 5         map.put(3, "cccc");
 6         System.out.println(map);
 7 // 第二种方式:
 8         // 通过values 获取所有值,不能获取到key对象
 9         // Collection<V> values()
10 
11         Collection<String> vs = map.values();
12         Iterator<String> it = vs.iterator();
13         while (it.hasNext()) {
14             String value = it.next();
15             System.out.println(" value=" + value);
16         }
17 }

第三种方式: Map.Entry

public static interface Map.Entry<K,V> 通过Map中的entrySet()方法获取存放Map.Entry<K,V>对象的Set集合。

Set<Map.Entry<K,V>> entrySet() 面向对象的思想将map集合中的键和值映射关系打包为一个对象,就是Map.Entry,将该对象存入Set集合,Map.Entry是一个对象,那么该对象具备的getKey,getValue获得键和值。

 1 public static void main(String[] args) {
 2         Map<Integer, String> map = new HashMap<Integer, String>();
 3         map.put(1, "aaaa");
 4         map.put(2, "bbbb");
 5         map.put(3, "cccc");
 6         System.out.println(map);
 7         // 第三种方式: Map.Entry对象 推荐使用 重点
 8         // Set<Map.Entry<K,V>> entrySet()
 9         
10 
11         // 返回的Map.Entry对象的Set集合 Map.Entry包含了key和value对象
12         Set<Map.Entry<Integer, String>> es = map.entrySet();
13 
14         Iterator<Map.Entry<Integer, String>> it = es.iterator();
15 
16         while (it.hasNext()) {
17             
18             // 返回的是封装了key和value对象的Map.Entry对象
19             Map.Entry<Integer, String> en = it.next();
20 
21             // 获取Map.Entry对象中封装的key和value对象
22             Integer key = en.getKey();
23             String value = en.getValue();
24 
25             System.out.println("key=" + key + " value=" + value);
26         }
27     }

Map三大常用功能方法表格整理

插入和删除元素表 2 )。

表 2:Map 更新方法: 可以更改 Map 内容。

clear()从 Map 中删除所有映射
remove(Object key)从 Map 中删除键和关联的值
put(Object key, Object value)将指定值与指定键相关联
clear()从 Map 中删除所有映射
putAll(Map t)将指定 Map 中的所有映射复制到此 map

查看 Map

表 3:返回视图的 Map 方法: 使用这些方法返回的对象,您可以遍历 Map 的元素,还可以删除 Map 中的元素。

entrySet()返回 Map 中所包含映射的 Set 视图。Set 中的每个元素都是一个 Map.Entry 对象,可以使用 getKey() 和 getValue() 方法(还有一个 setValue() 方法)访问后者的键元素和值元素
keySet()返回 Map 中所包含键的 Set 视图。删除 Set 中的元素还将删除 Map 中相应的映射(键和值)
values()返回 map 中所包含值的 Collection 视图。删除 Collection 中的元素还将删除 Map 中相应的映射(键和值)

访问元素

表 4:Map 访问和测试方法: 这些方法检索有关 Map 内容的信息但不更改 Map 内容。

get(Object key)返回与指定键关联的值
containsKey(Object key)如果 Map 包含指定键的映射,则返回 true
containsValue(Object value)如果此 Map 将一个或多个键映射到指定值,则返回 true
isEmpty()如果 Map 不包含键-值映射,则返回 true
size()返回 Map 中的键-值映射的数目

HashMap/Hashtable/ConcurrentHashMap/TreeMap/LinkedMap之间的区别

他们的区别主要在于线程安全的问题

HashMap:线程不安全,但效率快。如图1

Hashtable:线程安全,但效率慢。如图2

ConcurrentHashMap:采用分段锁机制,保证线程安全,效率又比较高,如图3

TreeMap:有序的排队集合

LinkedMap:采用哈下表+双向链表的结构,能够保证按元素添加的顺序进行存储。但是HashMap不会,比如说先存放的元素可能被存放于索引较后的数组中。

(图1)

如图1所示,ABC是同时独立操作数据,这样会造成数据错乱等线程不安全问题。比如:A往hashMap存了半碗水,然后B把A存的这半碗水取出来喝掉了,C往hashmap中存了半碗水。而A是不知道B,C的存在的,那么他就以为C存的那半碗水是自己存的那半碗。这样就是数据错乱问题。

hashmap允许同时操作数据,效率高,如果只是只做存或只做取数据时,hashmap不会有线程不安全问题。

图2

Hashtable采用的是顺序操作模式,能够有效防止数据错乱的问题,但是效率低。

图3

ConcurrentHashMap采用的是哈希表进行储存,首先通过对象数组的索引进行存储数据,这一步是不用顺序操作的,能够同步进行,因为数组的各个索引内容互不干扰。而在数组内部的哈希桶中,是采用顺序操作的方式,来保证数据不发生错乱。

哈希表的初始容量和散列因子

HashMap的实例有两个影响其性能的参数: 初始容量负载因子容量是哈希表中的桶数,初始容量只是创建哈希表时的容量。 加载因子是在自动增加容量之前允许哈希表获取的完整程度的度量。 当哈希表中的条目数超过加载因子和当前容量的乘积时,哈希表将被重新哈希(即,重建内部数据结构),以便哈希表具有大约两倍的桶数。

  • 散列因子越大,越节省空间,但查询效率低

  • 散列因子越小,越浪费空间,但查询效率高。

存储自定义对象

  • 当我们以HashMap存储数据时,不应该更改其key的值。

  • 如果确实要更改,那么就不要用hashMap这种数据结构来存储key。

HashMap<Book,String> data = new HashMap<Book, String>();
Book book1 = new Book("金苹果","讲述了苹果种植的辛酸路程");
data.put(book1,"我们人生的第一本书");
Book book2 = new Book("银苹果","讲述了苹果种植的辛酸路程");
data.put(book2,"我们人生的第二本书");
//哈希值是通过key和value计算出来的。
// 因为下面更改了key(book1),哈希值重新计算了,没有找到之前存放数据的哈希桶,从而没有找到value,返回null。
book1.setName("铜苹果");
System.out.println(data.get(book1));
//更可怕的是,当数据存到一定量时,哈希表重新散列。此时book1的键会重新按照铜苹果进行存储。
//这样又会出现我们一直以为key是金苹果,但实际是铜苹果这种尴尬的问题。
Book book3 = new Book("金苹果","讲述了苹果种植的辛酸路程");
//按道理,我们通过book3的键对去取,应该能找到Book1的value("我们人生的第一本书")
//除了要比较哈希值,系统还有比较equals。
// 虽然Book1和Book3的哈希值可能相同,但是因为Book1把name改为了铜苹果,所以equals为False,也是同样地找不到数据
System.out.println(data.get(book3));
//因此,当我们以HashMap存储数据时,不应该更改其key的值。
// 如果确实要更改,那么就不要用hashMap这种数据结构来存储key。

JDK 9 集合新特性

//JDK 9 集合新特性:List,Set,HashMap均有of()方法来设置内容
//集合长度一旦设定了,不可修改。
public static void main(String[] args) {
    List<String> list = List.of("锄禾日当午","汗滴禾下土");
    //list.add("谁知盘中餐");//由于集合长度一旦设定了,不可修改的特性,运行此代码时会报错。
    for (String s : list){
        System.out.println(s);
    }
​
    Set<String> set = Set.of("一二三四五","上山打老虎");
    for (String s : set){
        System.out.println(s);
    }
​
    Map<String,String> map = Map.of("haha1","锄禾日当午","haha2","汗滴禾下土");
    Set<String>  keyset = map.keySet();
    for (String key:keyset){
        System.out.println(key+"->"+map.get(key));
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值