集合框架简介

目录

前言

集合与数组的对比:

集合框架图:

Collection和Map

Collection:单列

List:有序列表集合

Queue:队列集合

Set:无序散列集合

Map:双列

Map集合的常用方法

集合的应用

暴力循环解:

使用单列集合:

使用双列集合:


前言

        操作同一类型数据或对象,在java中可使用一组数组来存放。但是,数组是固定长度的,使用数组进行开发会造成很多不便,比如资源的浪费,开发人员操作繁琐。

        为了解决这个问题,一种更优的解决方案“集合”出现了。Java 所有的集合类都位于 java.util 包下,提供了一个表示和操作对象集合的统一构架,包含大量集合接口,以及这些接口的实现类和操作它们的算法。

集合与数组的对比:

        1、数组声明了它容纳的元素的类型,而集合不声明。这是由于集合以object形式来存储它们的元素。

        2、一个数组实例具有固定的大小,不能伸缩。集合则使用初始容量和加载因子根据需要动态改变自己的大小。

        3、集合中不能放基本数据类型,但可以放基本数据类型的包装类。

集合框架图:

        上述类图中,实线边框的是实现类,比如ArrayList,LinkedList,HashMap等,折线边框的是抽象类,比如AbstractCollection,AbstractList,AbstractMap等,而点线边框的是接口,比如Collection,Iterator,List等。

Collection和Map

         java中的集合主要分为Collection和Map两种:

Collection:单列

        Collection 接口是 List、Set 和 Queue 接口的父接口,通常情况下不被直接使用。Collection 接口定义了一些通用的方法,通过这些方法可以实现对集合的基本操作。定义的方法既可用于操作 Set 集合,也可用于操作 List 和 Queue 集合。

Collection的常用方法:

boolean add(Object o)添加指定元素
boolean addAll(Collection c)添加指定集合
boolean remove(Object o)删除指定元素
boolean removeAll(Collection c)输出两个集合的交集
boolean retainAll(Collection c)保留两个集合的交集
void clear()清空集合
int size();集合中的有效元素个数
Object[] toArray()将集合中的元素转换成Object类型数组
boolean isEmpty();判断是否为空
boolean  containsAll(Collection c)判断是否包含指定集合
boolean contains(Object o);判断是否包含指定元素

List:有序列表集合

        特点:有索引值,有序,可重复;

        常用实现类:

ArrayList底层数据结构是数组,查询快,增删慢。 线程不安全,效率高。
Vector底层数据结构是数组,查询快,增删慢。 线程安全,效率低。
LinkedList集合数据存储的结构是链表结构。方便元素添加、删除的集合。查询慢,增删快。 线程不安全,效率高。

Queue:队列集合

        特点:队列的入口、出口各占一侧。

        常用实现类以及接口:

PriorityQueuePriorityQueue是一种比较标准的队列实现类,而不是绝对标准的。这是因为PriorityQueue保存队列元素的顺序不是按照元素添加的顺序来保存的,而是在添加元素的时候对元素的大小排序后再保存的。因此在PriorityQueue中使用peek()或pool()取出队列中头部的元素,取出的不是最先添加的元素,而是最小的元素。
Dueue接口与ArrayDeque实现类Deque接口是Queue接口的子接口,它代表一个双端队列;(有大量的首位操作方法)
LinkedList

LinkedList是List接口的实现类,因此它可以是一个集合,可以根据索引来随机访问集合中的元素。此外,它还是Duque接口的实现类,因此也可以作为一个双端队列,或者栈来使用。LinkedList与ArrayList,ArrayDeque的实现机制完全不同,ArrayList和ArrayDeque内部以数组的形式来保存集合中的元素,因此随机访问集合元素时有较好的性能;而LinkedList以链表的形式来保存集合中的元素,因此随机访问集合元素时性能较差,但是插入和删除元素时性能比较出色(只需改变指针所指的地址即可),需要指出的是,虽然Vector也是以数组的形式来存储集合但因为它实现了线程同步(而且实现的机制不好),故各方面的性能都比较差。

        Queue接口常用方法:

void add(Object e)将指定元素插入到队列的尾部
object element()获取队列头部的元素,但是不删除该元素。
boolean offer(Object e)将指定的元素插入此队列的尾部。当使用容量有限的队列时,此方法通常比add(Object e)有效。
Object peek()返回队列头部的元素,但是不删除该元素。如果队列为空,则返回null。
Object poll()返回队列头部的元素,并删除该元素。如果队列为空,则返回null。
Object remove()获取队列头部的元素,并删除该元素。

        Dueue接口的常用方法:

void add(Object e)将指定元素插入到队列的尾部。
object element()获取队列头部的元素,但是不删除该元素。
boolean offer(Object e)将指定的元素插入此队列的尾部。当使用容量有限的队列时,此方法通常比add(Object e)有效。
Object peek()返回队列头部的元素,但是不删除该元素。如果队列为空,则返回null。
Object poll()返回队列头部的元素,并删除该元素。如果队列为空,则返回null。
Object remove()获取队列头部的元素,并删除该元素。

Set:无序散列集合

        特点:没有索引值,不可重复;

        常用实现类:

HashSet无序,底层是哈希表,线程不安全,效率高
LinkedHashSet有序,底层是链表+哈希表
TreeSet可以排序,底层是红黑树
Hashtable和HashSet相似,线程安全,效率低

        set集合的新增过程:        

1、创建set集合,底层会创建一个数组;

2、添加元素时,新增元素的索引=该元素.hashCode()%数组长度,若该位置存在重复元素,则不添加;若不存在重复元素,则以链表形式挂在该索引位置下;

3、若同一索引位置添加的元素大于8个,则该链表链接会转换成为红黑树;

        判断重复元素的依据:        

哈希值相同&&( 地址值相同 || equal() 相同);

        通过哈希表结构可以保证的键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。

Map:双列

        特点:

1.一个元素是有一个K,一个V两部分组成

2.K  V可以是任意的引用数据类型

3.一个K对应唯一的一个V。K不能重复,V可以重复

         常用实现类:   

HashMap底层是哈希表。无序,线程不安全,效率高
LinkedHashMap底层是链表+哈希表。有序
TreeMap底层是红黑树。可排序
Hashtable线程安全,效率低和HashMap类似

             

Map集合的常用方法

public V put(K key, V value)把指定的键与指定的值添加到Map集合中。
public V remove(Object key)把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的值。
public V get(Object key)根据指定的键,在Map集合中获取对应的值。
boolean containsKey(Object key)判断集合中是否包含指定的键。
public Set<K> keySet():获取Map集合中所有的键,存储到Set集合中。
public Set<Map.Entry<K,V>> entrySet():获取到Map集合中所有的键值对对象的集合(Set集合)。

集合的应用

采用leetCode题库讲解:

        给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target  的那 两个 整数,并返回它们的数组下标。

        你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

        你可以按任意顺序返回答案。

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。


示例 2:

输入:nums = [3,2,4], target = 6
输出:[1,2]


示例 3:

输入:nums = [3,3], target = 6
输出:[0,1]

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/two-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

暴力循环解:

class Solution {
    public int[] twoSum(int[] nums, int target) {
        int[] res = new int[2];
        for(int i=0;i<nums.length;i++){
            for(int j=i+1;j<nums.length;j++){
                if(nums[i]+nums[j]==target){ //获取索引值强行遍历
                    res[0]=i;
                    res[1]=j;
                    break;
                }
            }
        }
        return res;
    }
}

使用单列集合:

class Solution {
    public int[] twoSum(int[] nums, int target) {
        int[] res = new int[2];
        ArrayList<Integer> list = new ArrayList<Integer>();
        for(int i=0;i<nums.length;i++){
            if(list.contains(nums[i])){        //如果等于补数
                res[0]=list.indexOf(nums[i]);  //获取补数的索引
                res[1]=i;                      //获取索引
            }
            list.add(target-nums[i]);          //不存在添加补数
        }
        return res;
    }
}

使用双列集合:

class Solution {
   public static int[] twoSum(int[] nums, int target) {
		int[] arr = new int[2];
		Map<Integer, Integer> map = new HashMap<>();

		for (int i = 0; i < nums.length; i++) {
			// 如果包含,,获取map的下标和i,即是索引值
			if (map.containsKey(nums[i])) {
				arr[0] = map.get(nums[i]); // 补数的下标
				arr[1] = i;
				return arr;
			}
			// 如果不包含,补数作为key,索引值作为value
			map.put(target - nums[i], i);
		}
		return null;
	}
}

        表面上看,第一种解法执行效率 < 第二种解法执行效率 < 第三种解法执行效率;实际上却不是如此;

暴力循环解:至少2个for循环,效率最低;若数组长度为x,则循环需要执行x²次;

使用单列集合:表面上时执行了1次for循环,然而list.contains() 方法底层其实执行的也是for循环,参考下图:

实际效率与第一种差不多;

使用双列集合:补数作为map的key,索引值作为value,只需要遍历一次,效率大大提升;

  • 13
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值