集合基础知识

对象数组

泛型

RandomAccess

  • 该接口啥也没干,只是标识实现该接口的实现类具有快速随机访问功能,数组可以,链表不行

集合

1.由来

Java是面向对象语言,需要一个容器存储对象--->StringBufferStringBuffer的结果是一个字符串--->对象数组
但对象数组的长度是固定的--->集合

2.数组和集合的差别

  • 长度区别
数组长度固定
集合长度可变
  • 内容不同
数组存的是同一类型的元素
集合可以存的不同类型的元素
  • 元素的数据类型不同
数组可以存基本数据类型,也可存引用数据类型
集合只能存引用数据类型

3.集合注意事项

  • 集合判空
使用isEmpty()而不是size==0,isEmpty()可读性更强且时间复杂度为O(1)
  • 集合转Map
toMap(),注意value!=null,否则抛异常
  • 集合遍历
注意并发修改异常,避免在遍历过程中使用add/remove,多线程情况下对Iteractor加锁
  • 集合去重
利用Set唯一性,HashSet依赖contains(),contains又依赖HashMapcontainsKey(),时间复杂度接近O(1)
Listcontains()通过遍历所有元素完成,时间复杂度接近O(n)
  • 集合转数组
toArray(T[] array),其中T为了标识返回类型,参数数组长度为0
  • 数组转集合
Arrays.asList()只用于对象数组,且注意asList()返回的是Arrays的内部类,其并未重写add()/remove()/...方法

4.集合继承体系

在这里插入图片描述

Collection

1.功能概述

  • 添加功能
boolean add(Object obj)
	添加一个元素
boolean addAll(Collection c)
	添加集合的所有元素到调用对象中()
  • 删除功能
void clean()
	移除所有元素
boolean remove(Object o)
	移除一个元素
boolean removeAll(Collection c)
	移除集合的全部or某些元素到调用对象中()
		只有一个元素相同时也返回true,只要有一个被移除了就是移除
  • 判断功能
boolean contains(Object o)
	判断集合中是否包含指定元素
boolean containsAll(Collection c)
	判断集合中是否包含指定的集合元素
		只有包含所有的元素才true
boolean isEmpty()
	判断集合是否为空
  • 长度功能
int size()
	元素的个数
面试题
	数组有没有length()方法?
	字符串有没有length()方法?
	集合有没有length()方法?
  • 交集功能
boolean retainAll(Collection c)
	两个集合都有的元素保存到调用对象中()
	假设有两个集合AB,AB做交集即A.retainAll(B),最终的结果保存在A中,B不变
	**返回值表示的是A是否发生过改变
  • 将集合转换为数组
Object[] toArray()
  • 获取功能(迭代器)
Iterator<E> iterator()
	集合的专用的遍历方式,依赖集合而存在
	方法
		Object next():获取元素,并移动到下一个元素位置
					即下次再使用此方法时,获取的是下一个元素
		hasNext():判断是否存在下个元素

在这里插入图片描述

2.遍历集合

  • 集合转数组
    在这里插入图片描述
  • 迭代器(集合专用遍历方式)
    在这里插入图片描述

3.Iterator解析

在这里插入图片描述

Collection儿子

1.List

  • 特点
有序(存储顺序和取出顺序一致)+ 可重复
  • 特有方法
添加功能
	void add(int index,Object element) 			
		指定位置添加元素,在index上加该元素
			注意index不要越界[0,size] 
				三个元素-->index[0,3]	
删除功能
	Object remove(int index) 			
		根据索引删除元素,返回被删除的元素
		注意index不要越界
获取功能
	Object get(int index) 			
		获取指定位置的元素,注意index不要越界	
修改功能
	Object set(int index,Object element) 			
		根据索引修改元素,返回被修改的元素 
		注意index不要越界
列表迭代器
	ListIterator listIterator() 
 		List集合特有的迭代器,继承自Iterator
 		特有成员方法
 			Object previous() 		
				获取上一个元素
			boolean hasPrevious() 		
					判断是否有元素
			注意:ListIterator可以实现逆向遍历,但必须先正向遍历,才可逆向遍历,所以一般无意义不不使用
			     但它的方法比Iterator多,且Iterator不可以实现逆序
  • 遍历
    在这里插入图片描述
    在这里插入图片描述

2.迭代器并发修改异常

  • ConcurrentModificationException
  • 产生原因
    在这里插入图片描述
迭代器是依赖于集合而存在的,在判断成功后,集合的中新添加了元素,而迭代器却不知道,所以就报错了,这个错叫并发修改异常
问题描述:迭代器遍历元素的时候,通过集合是不能修改元素的
  • 解决办法
    在这里插入图片描述

2.Set

  • 特点
无序(存储和取出的元素不一致)+唯一(不可重复)
	虽然无序,但他有他自己的顺序,注意输出顺序正好与其输出顺序一致的情况

List儿子

1.ArrayList

  • 特点
1.底层结构是对象数组,查询快,增删慢
2.线程不安全(不同步),效率高
3.1.5倍扩容
  • 方法
add(T t)
get(int index)
remove(int index)
isEmpty()
clear()
size()
  • toString()源码解析
    在这里插入图片描述
  • 数组转ArrayList
1.new ArrayList(Arrays.asList("a","b","c"))
2.引用类型流:Arrays.stream(myArray).collect(Collectors.toList())
3.基本类型流:Arrays.stream(myArray).boxed().collect(Collectors.toList())

2.Vector

  • 特点
1.底层结构是数组,查询快,增删慢
2.线程安全(同步),效率低
3.2倍扩容
  • 方法
添加功能
	public void addElement(Object obj)——add()替代
获取功能
	public Object elementAt(int index)——get()替代
	public Enumeration elements()——Iterator iterator()替代
	boolean hasMoreElements()——hasNext()替代
	Object nextElement()——next()替代

3.LinkedList

  • 特点
1.底层结构是双向链表,查询慢,增删快 
2.线程不安全,效率高 
  • 方法
添加功能
	public void addFirst(Object e) 		
	public void addLast(Object e) 	
获取功能
	public Object getFirst() 		
	public Object getLast() 	
删除功能
	public Object removeFirst() 		
	public Object removeLast()
	

4.思考题

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

Set儿子

1.HashSet

  • 特点
无序唯一
底层HashMap
线程不安全
  • 遍历
不可以使用for循环(无序),但增强for循环可以
  • add()源码解析—>保证唯一性原理
    在这里插入图片描述
    在这里插入图片描述

2.LinkedHashSet

  • 底层LinkedHashMap
  • 线程不安全

3.TreeSet

  • 作用
对元素按某种规则排序,根据构造方法选择哪种排序
  • 特点
1.保证元素排序和唯一性(有序唯一)
2.底层数据结构:红黑树(自平衡二叉树)
3.线程不安全
  • 排序方式
1.自然排序:元素具备比较性
	TreeSet<Student> ts = new TreeSet<Student>();
	
	public class Student implements Comparable<Student> {
		@Override
		public int compareTo(Student s) {
			// 根据自定义排序规则
			// 主要条件
			int num = this.age - s.age;
			// 次要条件  num=0表示年龄相同
			int num2 = num == 0 ? this.name.compareTo(s.name) : num;
			return num2;
		}
	}
2.比较器排序:集合具备比较性
	TreeSet<Student> ts = new TreeSet<Student>(new MyComparator());

	public class MyComparator implements Comparator<Student> {
		@Override
		public int compare(Student s1, Student s2) {
			// 根据自定义排序规则this -- s1     s -- s2
			// 主要条件
			int num = s1.getAge() - s2.getAge();
			// 次要条件  num=0表示年龄相同
			int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
			return num2;
		}}

HashSet儿子

1.LinkedHashSet

  • 特点
1.元素有序唯一
2.由链表保证元素有序
3.由哈希表保证元素唯一

Map<k,v>

  • 键值对:键不能重复,一个键(k)对应一个值,值(v)可以重复

1.功能概述

  • 添加功能
V put(K key,V value)
	添加元素
	如果键是第一次存储,就直接存储元素,返回null
	如果键不是第一次存在,就用值把以前的值替换掉,返回以前的值
	
这个其实还有另一个功能?先不告诉你,等会讲
  • 删除功能
void clear()
	移除所有的键值对元素
V remove(Object key)
	根据键删除键值对元素,并返回值
  • 判断功能
boolean containsKey(Object key)
	判断集合是否包含指定的键
boolean containsValue(Object value)
	判断集合是否包含指定的值
boolean isEmpty()
	判断集合是否为空
  • 长度功能
int size()
	返回集合中的键值对的对数
  • 获取功能
Set<Map.Entry<K,V>> entrySet()
	???
V get(Object key)
	根据键获取值,没有就返回null
Set<K> keySet()
	获取集合中所有键的集合
Collection<V> values()
	获取集合中所有值的集合

2.遍历键值对

在这里插入图片描述

  • 根据键找值
A:获取所有的键
B:遍历键的集合,获取得到每一个键
C:根据键去找值
--循环中有使用map.get()寻找值,加大时间复杂度,entrySet性能更佳

在这里插入图片描述

  • 根据键值对对象找键和值
A:获取所有键值对对象的集合
B:遍历键值对对象的集合,得到每一个键值对对象
C:根据键值对对象获取键和值

在这里插入图片描述

Map儿子

1.HashMap

  • 线程不安全,效率高
  • 其键值都可以为null
  • 子类依靠hashCode(),equals()找出重复元素(注意要重写这两个方法)
  • 底层
1.底层有哈希表实现,key的hashCode通过扰动函数获取hash值,通过(n-1)&&hash判断元素存储位置,若当前存在元素则比较key,key相同则覆盖,不同则拉链
2.扰动函数:HashMaphash(),减少哈希冲突,扰动实际是无符号右移,JDK8以前有4次扰动,之后只有一次,性能更佳
3.解决哈希冲突:若链表长度大于某阈值(默认8),调用treeifyBin()判断数组长度n,若n<64对数组2倍扩容,否则将链表转为红黑树
4.HashMap的长度为2的整数次幂,因为hash值范围加起来有40亿映射空间,内存存不下,所以hash值不能直接使用,要经过(n-1)&&hash
5.HashMap多线程导致死循环,因为并发下的Rehash造成循环链表,并发下建议使用ConcurrentHashMap

2.TreeMap

  • 键底层数据结构:红黑树(自平衡二叉树)
  • 保证键排序和唯一性
  • 排序:重写compareTo()

3.HashTable

  • 线程安全,效率低
  • 键值都不可以为null
  • 特有方法
load() 		
	从文件输入流中加载配置信息 	
store() 		
	保存到文件输出流中 	
getProperty()
setProperty(key,value) 		
	添加键值

HashMap儿子

1.LinkedHashMap

  • 特点
1.键有序唯一
2.由链表保证键有序(存储和取出的顺序一致)
3.由哈希表保证键唯一

综上案例

1.统计字符串中字符出现的个数

  • 分析
    在这里插入图片描述
  • 代码
// 定义一个字符串(可以改进为键盘录入)
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个字符串:");
String line = sc.nextLine();

// 定义一个TreeMap集合
TreeMap<Character, Integer> tm = new TreeMap<Character, Integer>();

//把字符串转换为字符数组
char[] chs = line.toCharArray();

//遍历字符数组,得到每一个字符
for(char ch : chs){
	//拿刚才得到的字符作为键到集合中去找值,看返回值
	Integer i =  tm.get(ch);
	
	//是null:说明该键不存在,就把该字符作为键,1作为值存储
	if(i == null){
		tm.put(ch, 1);
	}else {
		//不是null:说明该键存在,就把值加1,然后重写存储该键和值
		i++;
		tm.put(ch,i);
	}
}

//定义字符串缓冲区变量
StringBuilder sb=  new StringBuilder();

//遍历集合,得到键和值,进行按照要求拼接
Set<Character> set = tm.keySet();
for(Character key : set){
	Integer value = tm.get(key);
	sb.append(key).append("(").append(value).append(")");
}

//把字符串缓冲区转换为字符串输出
String result = sb.toString();
System.out.println("result:"+result);

2.模拟斗地主洗牌、发牌并对发好的牌排序

  • 分析
    在这里插入图片描述

  • 代码

public static void main(String[] args) {
	// 创建一个HashMap集合
	HashMap<Integer, String> hm = new HashMap<Integer, String>();

	// 创建一个ArrayList集合
	ArrayList<Integer> array = new ArrayList<Integer>();

	// 创建花色数组和点数数组
	// 定义一个花色数组
	String[] colors = { "♠", "♥", "♣", "♦" };
	// 定义一个点数数组
	String[] numbers = { "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q",
			"K", "A", "2", };

	// 从0开始往HashMap里面存储编号,并存储对应的牌,同时往ArrayList里面存储编号即可。
	int index = 0;

	for (String number : numbers) {
		for (String color : colors) {
			String poker = color.concat(number);
			hm.put(index, poker);
			array.add(index);
			index++;
		}
	}
	hm.put(index, "小王");
	array.add(index);
	index++;
	hm.put(index, "大王");
	array.add(index);

	// 洗牌(洗的是编号)
	Collections.shuffle(array);

	// 发牌(发的也是编号,为了保证编号是排序的,就创建TreeSet集合接收)
	TreeSet<Integer> fengQingYang = new TreeSet<Integer>();
	TreeSet<Integer> linQingXia = new TreeSet<Integer>();
	TreeSet<Integer> liuYi = new TreeSet<Integer>();
	TreeSet<Integer> diPai = new TreeSet<Integer>();

	for (int x = 0; x < array.size(); x++) {
		if (x >= array.size() - 3) {
			diPai.add(array.get(x));
		} else if (x % 3 == 0) {
			fengQingYang.add(array.get(x));
		} else if (x % 3 == 1) {
			linQingXia.add(array.get(x));
		} else if (x % 3 == 2) {
			liuYi.add(array.get(x));
		}
	}

	// 看牌(遍历TreeSet集合,获取编号,到HashMap集合找对应的牌)
	lookPoker("风清扬", fengQingYang, hm);
	lookPoker("林青霞", linQingXia, hm);
	lookPoker("刘意", liuYi, hm);
	lookPoker("底牌", diPai, hm);
}

// 写看牌的功能
public static void lookPoker(String name, TreeSet<Integer> ts,
		HashMap<Integer, String> hm) {
	System.out.print(name + "的牌是:");
	for (Integer key : ts) {
		String value = hm.get(key);
		System.out.print(value + " ");
	}
	System.out.println();
}

Collections

  • 针对集合进行操作的工具类,都是静态方法

1.面试题

在这里插入图片描述

2.成员方法

public static <T> void sort(List<T> list)
	排序
	默认情况下是自然顺序  Collections.sort(list);
	比较器排序  Collections.sort(list, new Comparator<Student>() {
					@Override
					public int compare(Student s1, Student s2) {
						...
					}
				});
	
public static <T> int binarySearch(List<?> list,T key)
	二分查找
public static <T> T max(Collection<?> coll)
	最大值
public static void reverse(List<?> list)
	反转
public static void shuffle(List<?> list)
	随机置换顺序

面试题

在这里插入图片描述

1.Queue与Deque区别

  • Queue:单端队列,只能在一端插入和删除
  • Deque:双端队列

2.ArrayDeque和LinkedList区别

  • ArrayDeque:基于数组和双指针,不支持null,支持扩容
  • LinkedList:基于链表,支持null,不支持扩容,每次插入都要申请新空间,性能低下

3.Priority特点

  • 基于二叉堆(默认小顶堆)数据结构,可变长度数组存储数据
  • 线程不安全,不支持null对象

4.ConcurrentHashMap与HashTable保证线程安全区别

  • ConcurrentHashMap底层使用可变长度数组与链表,将整个数组分段,每段一把锁,访问不同数据段时不涉及锁竞争,提高并发量,改进后使用Node+链表/红黑树,并发控制采用synchronized+CAS
  • Hashtable底层只采用一把锁,效率低下

5.ConcurrentHashMap底层具体实现

  • 原来底层采用Segment数据与HashEntry数组
Segment继承自ReentrantLock(可重入锁)
一个ConcurrentHashMap含多个Segment(分段数组)
Segment数组底层结构与HashMap相似(,)
Segment不可扩容,默认16,即最多支持16个线程并发
----------------------------------------------
HashEntry是链表中的元素节点
一个一个Segment含多个HashEntryHashEntry的修改操作前需要获取SegmentHashEntry可扩容
  • 之后取消Segment部分,改用synchronized+CAS
只锁定当前链表或红黑树的头节点,认为不存在哈希冲突就不存在并发
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值