Java基础 | Collection

1.集合概述

为了方便多个对象操作,需要对它们进行存储

相对于数组来说集合就相当于数组的升级版

集合就像是一个容器,可以动态的把多个对象放在容器中

同时Collection接口也是单例模式的体现

1.1集合与数组对比

数组

基本/引用数据类型都OK
长度固定 不能自动增长,有序 可重复
用于元素固定的情况
提供的方法有限,很多功能需要我们自己去定义

集合

基本(指包装类)/引用数据类型都OK
长度可变 根据元素增长而增长,可以有序可以无序
用于元素不固定的情况
提供了丰富的方法,使用时直接调用即可

1.2体系图

集合接口和各种实例类之间的关系图

2.Collection接口

2.1通用方法

适用于常用的List Set Map接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//添加元素
s.add("ho");
//删除其中指定字符
s.remove("oooo");
//判断是否包含
s.contains("aaa")
//判断是否为空字符串
s.isEmpty()
//判断字符长度
s.size()
//清空集合元素
s.clear();

//cl1所有元素添加到cl2集合
cl1.addAll(cl2);
//把集合a和b的差集删除
boolean boo = a.removeAll(b);
//判断c1是否包含c2
boolean boo = c1.containsAll(c2);
//取交集,最终的结果赋给了c1
boolean boo = c1.retainAll(c2);
//比较两个元素
a.equals(b);
//获取hash码
c1.hashCode()

2.2数组转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//集合-->数组
//使用collection下的通用方法toArray()
ArrayList arr = new ArrayList();
arr.add("H");
arr.add("i");
Object[] obj = arr.toArray();
for (int i = 0; i < obj.length; i++) {
System.out.print(obj[i]+"、");
}

//数组-->集合
使用Arrays的方法asList()
List asList = Arrays.asList(new String[]{"a", "c"});
System.out.println(asList);

2.3迭代器

  1. iterator用于遍历集合,同时也是设计模式的一种
  2. 通过提供一种方法去访问容器对象中的元素
  3. 这样做,不会去暴漏该对象的内部细节
  4. hashNext和next是最常用的方法
  5. iterator不是容器,实际的容器是集合对象
  6. iterator只负责对集合中的元素进行条件迭代
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    //迭代器遍历
    public static void IteratorTravers(Collection c){
    //迭代器初始化调用collection下的iterator方法
    Iterator ddq = c.iterator();
    //判断是否还有下一个元素
    while (ddq.hasNext()){
    //next让指针下移,下移以后集合位置上的元素返回
    System.out.println(ddq.next());
    }
    }
    //foreach遍历 JDK5+
    for (集合元素类型 局部变量:集合对象){
    System.out.print(要遍历的结构名称+" ");
    }

3.List接口

相同点都实现了List,存储有序的,可重复的

三者的异同

ArrayList JDK1.2 线程不安全 作为List主要的实现类

Vector JDK1.0 线程安全 已经接近于Deprecated

LinkedList JDK1.2 底层为双向链表 频繁插入,删除操作,使用更多

3.1通用方法

  • collection接口方法通用
  • 添加元素所属类一定要重equals方法
    1
    2
    3
    4
    5
    6
    7
    //1.获取指定索引元素
    elements.get(2)
    //2.指定元素首次出现或末尾出现索引值(可重复性)
    elements.indexOf(2262)
    elements.lastIndexOf(2262)
    //3.包括头不包括尾[左闭右开]
    elements.subList(1,3)

3.2ArrayList

JDK7类似饿汉式

  1. 底层默认创建了长度为10的Object类型数组
  2. 如果add的元素超过默认长度,就会自动扩容
  3. 默认扩容为原数组容量的1.5倍,原有的自动赋值到新数组中
  4. 若元素固定,建议使用带参构造器

JDK8类似懒汉式

  1. 底层数组的初始化是{ },并没有创建长度为10的数组
  2. 当调用add方法时,才会创建长度为10的数组
  3. 后续和7基本一致

3.3LinkedList

底层为双向链表可以很方便的头尾插入删除的数据

  1. 创建一个LinkedlList对象后,底层创建了两个Node类型的属性
  2. 属性为next和prev,此时默认的值为null
  3. 调用add方法后把元素封装到Node中,此时创建了Node对象
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //Node的定义
    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;
    }
    }

4.Set接口

相同点都实现了Set,存储无序的,不可重复的

Set没定义新的方法,都使用Collection接口的方法

三者的异同

  1. HashSet 底层数组+链表(8之后改为HashMap)

  2. HashSet set接口的主要实现类,线程不安全,可存放null值

  3. LinkedHashSet HashSet的子类 底层为双向链表 根据数据排序

  4. 有序为假象 因为存储的数据同时维护了两个引用用链接前后数据

  5. TreeSet 底层红黑树 可按照添加对象的指定属性,进行排序

4.1无序性

以HashSet为例 不等同于随机性

所存储元素在底层数组中不是按照索引排列添加

而是按照哈希值来决定具体的顺序

4.2不可重复

添加的元素按照equals判断时,不可返回true

即,相同的元素只能添加一个

4.3HashSet

存储过程

add元素a,会调用元素a类中的hashCode( ),计算哈希值

哈希值通过算法计算出在HashSet底层数组中存放位置(即索引)

判断数组这个索引位置有没有值

若无就添加成功,若有单个或多个链表形式的元素,比较哈希值

若哈希值不同添加成功,若相同,继续调用元素a类中的equals方法

返回true添加失败 返回false添加成功

添加元素所属类一定要重写hashCode和equals且尽可能保持一致

七上八下
Jdk7 元素a放在数组中,指向原来索引
JDK8 原来元素放在数组中,指向元素a

4.4TreeSet

底层 红黑树 可按照添加对象的指定属性,进行排序

TreeSet中添加的元素类型必须为相同类

和比较器一样提供了两种排序方法 自然排序和定制排序

定制排序把排序参数放在TreeSet构造器中即可不影响自然排序

其实就是对Comparable和Comparator的具体使用

5.Map接口

  • JDK7以前为 数组+链表
  • JDK8开始为 数组+链表+红黑树
  • Map双列数据 存储为key-value对的数据
  • 底层都是由哈希算法实现的

三者异同处

  1. HashMap Map主要实现类 线程不安全 可存放null的键值对
  2. LinkedHashMap ↖子类 可根据数据添加顺序排序 底层双向链表
  3. Hashtable 线程安全 不可存放null的键值对 基本弃用
  4. Properties ↖子类 常用于处理配置文件 键值对都为String类型
  5. TreeMap 按照键值对中键进行排序 实现排序遍历 底层红黑树

5.1键值对理解

键值对数据结构图形

  • key是无序的 不可重复的 使用set存储
  • value是无序的 可重复的 使用collection存储
  • key和value构成entry对象
  • entry特点:无序的 不可重复的 使用set存储所有entry
  • HashMap 若key为自定义类,必须重写equals和hashCode
  • TreeMap 需考虑比较器的问题

5.2通用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Map map = new HashMap();
map.put("Ho",15333); //添加
map.putAll(map2); //添加一个map到另外一个map
map.remove("Po",13); //删除
map.size(); //键值对的数量
map.clear(); //清楚
map.containsKey("A"); //是否包含指定key
map.containsValue("2333"); //是否包含指定value

map.isEmpty(); //判断是否为空
map.equals(map2); //判断两个map元素是否全部相等
map.values(); //获取所有value
map.keySet(); //获取所有key
in.entrySet(); //用于遍历
Entry.getKey(); //Entry接口方法获取key
Entry.getValue(); //Entry接口方法获取value

5.3Map遍历

分别遍历

  1. values方法获取所有value元素
  2. keySet方法获取所有key元素
  3. 通过Collection或Set存放
  4. 使用迭代器或foreach进行遍历
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    HashMap map = new HashMap();
    map.put("河南",41);
    map.put("江苏",32);
    map.put("山东",34);
    map.put("浙江",33);
    Collection mapV = map.values();
    for (Object obj : mapV) {
    System.out.println(obj);
    }

    HashMap map = new HashMap();
    map.put("河南",41);
    map.put("江苏",32);
    map.put("山东",34);
    map.put("浙江",33);
    Set mapS = map.keySet();
    for (Object obj : mapS) {
    System.out.println(obj);
    }
    组合遍历
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    //方式一:通过key获取value
    Map<String,Integer> datas = new HashMap<>();
    datas.put("Po",13);
    datas.put("Lo",14);
    datas.put("Ho",15);
    Set<String> sets = datas.keySet();
    Iterator<String> iterator = sets.iterator();
    while (iterator.hasNext()){
    //key值
    Object key = iterator.next();
    //通过key获取value
    Object value = datas.get(key);
    System.out.println(key+"-->"+value);
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //方式二:使用EntrySet方法
    //1.通过entrySet获取一个Set类型的变量
    Set<Map.Entry<String, Integer>> entries = datas.entrySet();
    //2.调用迭代器
    Iterator<Map.Entry<String, Integer>> iterator = entries.iterator();
    while (iterator.hasNext()){
    //3.把iterator转换为Entry类型
    Map.Entry<String, Integer> entryData = iterator.next();
    //4.调用Entry中方法,完成key_value的遍历
    System.out.println(entryData.getKey()+"->"+entryData.getValue());
    }
    1
    2
    3
    4
    5
    6
    //方式三:使用foreach
    //foreach遍历 需确定泛型
    HashMap<String,Integer> datas = new HashMap<>();
    for (Map.Entry<String,Integer> Entry :datas.entrySet()){
    System.out.println(Entry.getKey()+"="+Entry.getValue());
    }

5.4HashMap

实现原理JDK7

  • 实例化结束默认创建16位的数组Entry[] table 并Put一个键值对
  • 首先会调用key所在类的hashCode()计算哈希码
  • 通过算法得到在Entry数组中的位置
  • 若位置为空 key-value存放成功
  • 若位置不为空 比较key和已经存在数据的哈希值
  • 若哈希值也不相同 key-value存放成功 情况2
  • 若和某一个数据哈希值相同 使用key所在类的equals继续比较
  • 返回false key-value存放成功 情况3
  • 返回true 添加的替换掉已有的
  • 情况2和3 此时的key-value和原有数据都是用链表存放
  • 当超出临界值(12,并要存放位置非空)默认扩容量:初始化的2倍

JDK8以后变化

  • 只有调用put方法时 才会创建16位的数组 名称由Entry改为Node
  • 底层的结构增加了红黑树 主要为了提高查找效率
  • 当数组上有一索引位置上元素以链表存储个数 >8 && 数组length>64
  • 此时索引位置上存储结构改为红黑树

5.5TreeMap

  • 底层 红黑树 按照键值对中键进行排序 实现排序遍历
  • key必须为同一个类创建的对象
  • 和TreeSet一样分自然排序和定制排序
  • 和比较器一样提供了两种排序方法 自然排序和定制排序
  • 定制排序把排序参数放在TreeSet构造器中即可不影响自然排序
  • 其实就是对Comparable和Comparator的具体使用

5.6LinkedHashMap

  • 频繁遍历可多使用LinkedHashMap
  • 通过它的可得知继承 了HashMap类中的Node方法
  • 同时多了before和after两个指针用于记录添加元素的先后顺序
  • 最终达到遍历时有序的一种效果
    1
    2
    3
    4
    5
    6
    7
    8
    9
    /**
    * HashMap.Node subclass for normal LinkedHashMap entries.
    */
    static class Entry<K,V> extends HashMap.Node<K,V> {
    Entry<K,V> before, after;
    Entry(int hash, K key, V value, Node<K,V> next) {
    super(hash, key, value, next);
    }
    }

5.7Properties

主要用于一些配置文件的获取

具体使用到JDBC会提起

1
2
3
4
5
6
7
8
9
Properties p = new Properties();
//1.加载配置文件
p.load(new FileInputStream("d_Collections/jdbc.properties"));
//2.通过key获取配置文件value
p.getProperty("name");
p.getProperty("age");
p.getProperty("school");
//3.输出
System.out.println(p);

6.Collections类

  1. 和Arrays相同提供了Collection和Map通用的静态方法
  2. 为线程不安全的类提供了相对应的安全方法

6.1常用方法

1
2
3
4
5
6
7
Collections.sort(data); //排序 默认自然排序
Collections.shuffle(data); //打乱集合元素
Collections.swap(dataB,0,1); //交换指定索引位置
Collections.binarySearch(al,"g"); //二分查找
List list = Arrays.asList(new Object[al.size()]);
Collections.copy(list,al); //赋值集合1全部元素到集合2
Collections.frequency(al, "c"); //相同元素出现的次数

6.2线程安全相关

1
2
3
4
5
//synchronizedXxx xxx即对应集合接口的方法
List list = Collections.synchronizedList(arr);
Collection col = Collections.synchronizedCollection(arr2);
Map newmap = Collections.synchronizedMap(map);
Set sets = Collections.synchronizedSet(arr3);

7.优选文献

  1. CSDN luowei201711 简易理解集合
  2. CSDN phial03 高赞高浏览 细谈集合

Java基础 | Collection
http://example.com/2022/05/23/Java初级部分/SEImprove/JavaCollection/
Author
John Doe
Posted on
May 23, 2022
Licensed under