笔记整理: Java中的集合框架

概念:
集合类存放于 Java.util 包中,
主要有 3 种:set、list和 map

       1. Collection 是集合 List、Set、Queue 的最基本的接口。
       2. Iterator:迭代器,可以通过迭代器遍历集合中的数据
       3. Map:是映射表的基础接口

整个集合框架关系图:在这里插入图片描述
Iterable 接口
实现此允许对象成为for-Each循环的目标, 也就是增强for循环,这是Java中的一种语法糖,数组同样也可以使用for-each循环遍历;

int[] num = {1,2,3,4};
for(int i :num){
    System.out.println(i);
}

Collection父接口
Collection 是一个顶层接口,主要是定义集合的约定,
List接口List接口对Collection进行了简单的扩充,它的具体实现类常用的有ArrayListLinkedList
Set接口也是Collection的一种扩展,而与List不同的时,在Set中的对象元素不能重复,也就是说你不能把同样的东西两次放入同一个Set容器中。它的常用具体实现有HashSetTreeSet类。
Map是一种把键对象和值对象进行关联的容器,而一个值对象又可以是一个Map,依次类推,这样就可形成一个多级映射。
在这里插入图片描述

这张图看起来就特别香,至于他们之间的区别,还是用表格比较清楚:

是否包含重复元素是否继承至Collection元素是否有序
List可以重复有序
Set不可重复hashSet元素可以无序,但是元素不可重复;TreeSet的元素可以实现有序
Mapkey不可重复,value可以重复hashMap是无序的,LinkedHashMap和TreeMap又可以是有序的

实现差异

1. ArrayList()跟LinkedList()

ArrayList底层是基于数组实现的,所以数组在查询的时候很方便,但是添加或者删除元素很慢,另外,Vector也是跟ArrayList一样基于数组实现,但是Vector是线程安全的集合.ArrayList不是
LinkedList底层是基于双向链表来实现的,相对于在添加或者删除元素等操作来讲十分方便快捷,但是查询起来比较麻烦

2. HashSet()跟TreeSet()

HashSet 底层用到了hash算法,通过给内存地址取余进行编号的方式,方便元素的查找.但是它不能保证集合的迭代顺序,但是HashSet允许又null元素
TreeSet的元素可以实现有序,但是需要实例类(比如自定义的一个Student类)自己去实现compareable<T>接口,并且重写compareTo()方法.

2. HashMap()跟TreeMap()

HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。
根据键的 HashCode 值存储数据,具有很快的访问速度,最多允许一条记录的键为 null,不支持线程同步
HashMap 是无序的,即不会记录插入的顺序
HashMap 的 key 与 value 类型可以相同也可以不同,可以是字符串(String)类型的 key 和 value,
也可以是整型(Integer)的 key 和字符串(String)类型的, value也可以是整型(Integer)的 key 和字符串(String)类型的 value键无序,并且不能重复, 值可以重复

TreeMap底层是基于红黑树实现的map集合, 这个map根据key自然排序存储,或者通过comparator进行定制排序 ,非线程安全的
在这里插入图片描述
通用方法

List<Integer> list = new ArrayList<>();
        // 添加元素 一个参数,就在集合的尾部添加元素
        list.add(5);
        list.add(6);
        list.add(7);
        // 两个参数 add(index, obj) 表示在指定位置,添加元素,该位置原来的元素依次向后挪动位置
        list.add(1, 4);// 5 4 6 7
        // 如果指定位置超出了原来集合的长度,就会报数组越界异常
        //list.add(5, 4);// IndexOutOfBoundsException

        // 获取集合长度
        System.out.println(list.size());// 4

        // 获取指定的元素的下标
        System.out.println(list.indexOf(5)); //0

        // 如果指定元素不存在,就会返回-1
        System.out.println(list.indexOf(3)); // -1

        // 获取指定位置的元素
        System.out.println(list.get(3));
        // 如果指定下标位置超出集合长度-1,就会报数组越界异常IndexOutOfBoundsException
        System.out.println(list.get(3));

        // 判断集合是否为空
        System.out.println(list.isEmpty());//false

        // 判断集合中是否包含某个元素 返回的是一个Boolean值 true/false
        System.out.println(list.contains(10));

        // 遍历list元素并打印输出
        for (Integer integer : list) {
            System.out.println(integer);
        }
        // 清除元素
        list.clear();
        System.out.println(list.isEmpty());//true

遍历集合的方法

for循环:适用于有下标的集合类型

List<Integer> list = new ArrayList<>();
        list.add(5);
        list.add(6);
        list.add(7);
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }

for-each: 适用于无下标的集合

Set<Integer> set = new HashSet();
        set.add(3);
        set.add(5);
        set.add(4);
        // 因为Set集合元素是无序的,没有.get(index)获取元素的方法,所以for-each比较适合
        for (Integer i : set){
            System.out.println(i);
        }

iterator(): 集合通用,但是不推荐(写起来比较麻烦)

List<String> list1 = new ArrayList<String>();
        list1.add("hello");
        list1.add("world");
        Iterator<String> it = list1.iterator();
        while(it.hasNext()){
            String s = it.next();
            System.out.println(s);
        }

面试常见问题

1. Java 中Comparator 和Comparable 有什么不同?

Comparator 接口是用于定义对象的自然排序,是排序接口,而Comparable 通常是用于定义用户定制的顺序,是比较接口,
如果我们需要控制某个类的次序,而该类本身不支持排序(即没有实现Comparable接口)的时候,我们就可以建立一个该类的比较器来实现自定义排序,
Comparable总是只有一个,但是可以有多个comparator比较器来定义类的顺序

2. Java 集合框架的基础接口有哪些?

Collection 为集合层级的根接口,一个集合代表一组对象,
Set 是一个不能包含重复元素的无序集合,
List 是一个有序集合,可以包含重复元素,可以铜鼓它的索引来访问元素,List就像一个长度动态变换的数组
Map 是一个将key映射到value的对象,一个Map不能包含重复的key,每个key最多只能映射到一个value

3. Java 中Collection和Collections有什么区别?

Collection是一个集合接口,它提供了对集合对象进行基本操作的通用接口方法,所有集合都是它的子类,比如List ,Set等;
Collections 是一个包装类,它包含了很多静态方法,不能被实例化,是一个工具类,比如提供的排序方法: Collections.sort(list);

4. List, Set, Map 是否继承自Collection接口?

List Set是继承自Collection接口, Map不是,
Map是键值对映射容器,跟List和Set有明显的区别,而Set存储的是零散的元素并且不能重复,List是线性结构的容器,适用于按数值索引访问元素的情形

5. List, Set, Map 之间的区别是什么?

List, Set, Map 的区别主要是体现在两个方面: 元素是否有序, 元素是否允许重复
具体区别如图所示:
在这里插入图片描述

6. HashMap和HashTable的区别是什么?
  1. hashMap允许key和value的值为null,但是hashTable不允许
  2. hashTable是线程同步的,hashMap不是,所以 hashmap适合单线程,hashtable适合多线程环境
7. 什么时候用HashMap,什么时候用HashTable?

对于在Map中插入,删除,定位一个元素的操作比较多的话, hashMap是最好的选择,因为hashmap的插入会更快,如果要对一个key集合进行有序遍历的操作的话,treeMap会更好;

8. 简单概述一下HashMap和HashSet的实现原理

HashMap 是基于Hash算法实现的, 我们通过 put(key,value)来存储数据,通过get(key)来获取数据,
当我们传入key的时候,hashMap会根据key.hashCode()方法计算出hash值, 根据hash值,将value保存到bucket里面, 当计算出的hash值相同时,我们称之为 hash冲突, 出现hash冲突的时候,HashMap的做法是用链表和红黑树存储相同hash值的value, 如果这个hash冲突的个数比较少的情况下,就使用链表存储value的值,否则使用红黑树来存储.

HashSet是基于HashMap实现的, HashSet底层使用HashMap来保存所有元素,因此HashSet的实现比较简单,相关的hashSet操作,基本上都是直接调用hashMap的相关方法来完成的,HashSet不允许重复的值.

9. ArrayList和LinkedList的区别是什么?
  1. 数据结构: ArrayList底层是基于动态数组实现的, LinkedList是基于双向链表实现的
  2. 访问效率: ArrayListLinkedList在随机访问的时候效率要高,因为LinkedList是线性的数据存储方式,所以需要移动指针从前往后依次查找, ArrayList可以通过索引的方式直接访问元素
  3. 增加和删改效率: 在执行增加元素和删改元素的操作时,LinkedList要比ArrayList效率高,因为ArrayList增删改操作要影响数组内其他数据的下标.
  4. 总的来说,需要频繁读取集合中数据的时候,推荐使用ArrayList,在增删改操作比较多时候,推荐使用LinkedList.
10. Array和LinkedList有什么区别?
  • Array可以存储基本数据类型和引用数据类型(对象),ArrayList只能存储引用数据类型(对象)*
  • Array是指定固定大小的, ArrayList的长度是自动扩展的*
  • Array内置方法没有 ArrayList多,比如addAll(), removeAll()等等方法只有ArrayList才有*
11. 如何编写一个自定义的泛型类(Stack为例)?
  1. 创建一个IStack<E>泛型接口, 规定这个接口中需要被实现的基本方法
  2. 创建一个实现IStack<E>接口的实例类MyStack
  3. 测试MyStack类

//test.java

    public static void main(String[] args) {
        // 创建一个容器对象
        IStack<Integer> mysk = new MyStack<>();

        //添加三个元素到容器中
        mysk.push(5);
        mysk.push(6);
        mysk.push(7);

        // 查看容器是否为空
        System.out.println(mysk.isEmpty());// false

        // 查看容器是否放满
        System.out.println(mysk.isFull()); // false

        // 获取容器长度
        System.out.println(mysk.size()); // 2

        // 取出最后放入的元素
        System.out.println(mysk.pop()); // 6

        // 取出栈顶元素
        System.out.println(mysk.peek()); // 5

        // 取出下标为0 的元素
        System.out.println(mysk.get(0));//5

        // 获取元素55在容器中的下标  不存在返回-1
        System.out.println(mysk.getIndexOf(55)); // -1
        
        // 打印容器中所有元素
        mysk.display();  //
    }

// IStack.java

public interface IStack<E>{
	/**
	压栈  将传入进来的数据,放到容器数组当中
	*/
	public void push(E e);
	/**
	出栈 取出最后放入的数据,容器中的元素-1
	*/
	public E pop();
	/**
	取出栈顶元素
	*/
	public E peek();
	/**
	获取容器长度
	*/
	public int size();
	/**
	判断容器中是否为空  如果为空,返回true
	*/
	public boolean isEmpty();
	/**
	判断容器是否已经装满了,如果满了返回true
	*/
	public boolean isFull();
	/**
	获取指定下标的元素
	*/
	public E get(int index);
	/**
	根据传入的元素,获取该元素在容器中的位置(下标)  如果不存在,返回-1
	public int getIndexOf(E e);
	/**
	打印容器中所有元素
	*/
	public void display();
}

// MyStack.java

public class MyStack<E> implements IStack<E>{
	private Object[] data = null; // 用于存放数据的数组
	private int top = -1; // 用于记录栈顶的位置   默认-1就是栈顶
	private int maxSize ; // 栈顶最大容量
	private static final int DEFAULT_SIZE = 10; // 默认容器的初始大小为10
	
	public MyStack(){
		this(DEFAULT_SIZE);
	}
	
	public MyStack(int initialSize){
		if(initialSize > 0){
			this.maxSize = initialSize;
			this.data = new Object[initialSize];
		}
		else{
			this.maxSize = DEFAULT_SIZE;
			this.data = new Object[DEFAULT_SIZE];
		}
	}
	@Override
	public void push(E e){
		if(isFull()){
			System.out.println("栈空间已满,无法放入");
			return;
		}
		top++;
		data[top] = e;
	}
	@override
	public E pop(){
		if(isEmpty()){
			System.out.println("栈空间为空");
			throw new IndexOutBoundsEXception("栈为空");
		}
		E e = (E)data[top];
		top--;
		return e;
	}
	@override
	public E peek(){
		if(isEmpty()){
			System.out.println("栈空间为空");
			return null;
		}
		E e = (E)data[top];
		return e;
	}
	
	@override
	public int size(){
		return this.top+1;
	}
	
	@override
	public boolean isEmpty(){
		return this.top == -1;
	}
	@override
	public boolean isFull(){
		return this.top >= this.maxSize-1;
	}

	@override
	public E get(int index){
		if(index < 0 || index > top){
			throw new ArrayIndexOutBoundsEXception();
		}
		E e = (E) data[index];
		return e;
	}
	@override
	public int getIndexOf(E e){
		int temp = this.top;
		while(temp != -1){
			if(get(temp).equals(e)){
				return temp;
			}
			temp--;
		}
		return -1;
	}
	@override
	public void display(){
		int temp = this.top;
		while(temp != -1){
			System.out.println(data[temp]);
			temp--;
		}
	}
}

>好了就以华丽的分割线结束了,总结的比较浅略,等我理解更深的时候再来补充吧...
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

国服酱紫牙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值