Java集合详解
Posted JWei_7
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java集合详解相关的知识,希望对你有一定的参考价值。
Java集合详解
- 开篇解答 c++的stl容器和java集合的区别 以及相识之处
- 集合和数组的区别
- java集合分类
- 面试题:
开篇解答 c++的stl容器和java集合的区别 以及相识之处
(好了 总结一句 一个是java写的 一个是c++写的 很好 就是这样 皮一手)
集合和数组的区别
一、数组声明了它容纳的元素的类型,而集合不声明。
二、数组是静态的,一个数组实例具有固定的大小,一旦创建了就无法改变容量了。而集合是可以动态扩展容量,可以根据需要动态改变大小,集合提供更多的成员方法,能满足更多的需求。
三、数组的存放的类型只能是一种(基本类型/引用类型),集合存放的类型可以不是一种(不加泛型时添加的类型是Object)。
四、数组是java语言中内置的数据类型,是线性排列的,执行效率或者类型检查都是最快的。
java集合分类
Java 集合类型分为 Collection 和 Map,它们是 Java 集合的根接口,这两个接口又包含了一些子接口或实现类。图 1 和图 2 分别为 Collection 和 Map 的子接口及其实现类。
黄色块为集合的接口,蓝色块为集合的实现类
java集合类 分点做简单介绍 以及使用 介绍 优势以及劣势 以及 基本知识点
Conllection
Collection 接口是 List、Set 和 Queue 接口的父接口,通常情况下不被直接使用。Collection
接口定义了一些通用的方法,通过这些方法可以实现对集合的基本操作。定义的方法既可用于操作 Set 集合,也可用于操作 List 和 Queue
集合。
List
这里 我们介绍一下 Arraylist和LinkedList ,stack和vector 不做过多介绍 具体用法 和stl容器的差不多。
如何使用list接口
在Java中,必须导入 java.util.List 包才能使用List。
List<Integer> numbers = new LinkedList<>();
LinkedList<Integer> numbers = new LinkedList<>();
在这里,我们已经创建Vector,ArrayList和LinkedList类的对象。现在这些对象就可以使用List接口的功能。
List方法
List接口包括Collection接口的所有方法。 这是因为Collection是List的超级接口。
Collection接口中还提供了一些常用的List接口方法:
add() - 将元素添加到列表
addAll() - 将一个列表的所有元素添加到另一个
get() - 有助于从列表中随机访问元素
iterator() - 返回迭代器对象,该对象可用于顺序访问列表的元素
set() - 更改列表的元素
remove() - 从列表中删除一个元素
removeAll() - 从列表中删除所有元素
clear() - 从列表中删除所有元素(比removeAll()效率更高)
size() - 返回列表的长度
toArray() - 将列表转换为数组
contains() - 如果列表包含指定的元素,则返回true
list接口种的集合 通用!
List接口的实现
1.LinkedList
package com.example.demo;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Vector;
public class working
public static void main(String[] args)
//使用LinkedList类创建列表
List<Integer> numbers = new LinkedList<>();
//将元素添加到列表
numbers.add(1);
numbers.add(2);
numbers.add(3);
System.out.println("List: " + numbers);
//从列表中访问元素
int number = numbers.get(2);
System.out.println("访问元素: " + number);
//使用indexOf()方法
int index = numbers.indexOf(2);
System.out.println("位置3的元素是 " + index);
//从列表中删除元素
int removedNumber = numbers.remove(1);
System.out.println("删除元素: " + removedNumber);
//将列表转为数组
Object[] array = numbers.toArray();
System.out.print("剩余数组元素为:");
for(int i=0;i<array.length;i++)
System.out.print(array[i]+" ");
//换行
System.out.println();
//从列表中查询某数
System.out.println("能不能从数组中查询到1 "+numbers.contains(1));
(一)LinkedList的底层是什么?
双向链表
让我们来看看底层源码
private void linkFirst(E e)
final Node<E> f = first;
final Node<E> newNode = new Node<>(null, e, f);
first = newNode;
if (f == null)
last = newNode;
else
f.prev = newNode;
size++;
modCount++;
/**
* Links e as last element.
*/
void linkLast(E e)
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
(二)LinkedList能不能作为队列使用?
开头就说了 因为LinkedList是双向循环链表 所以它也可以当成队列,栈和双端队列来使用
首先我们先来说一下LinkedList关于队列操作的源码。
队列的基本方法
//定义
LinkedList<Integer> queue = new LinkedList<Integer>();
//添加元素
queue.add(1);
//删除队列头元素
queue.poll();
//获取队列头元素,不删除
queue.peek();
演示:
首先定义就不说了 直接看源码:
.add()//添加元素
public boolean add(E e)
linkLast(e); //添加在队尾
return true;
.poll()//删除队列头元素
// 删除并返回第一个节点
// 若LinkedList的大小为0,则返回null
public E poll()
if (size == 0)
return null;
return removeFirst();
.peek()//获取队列头元素,不删除
// 返回第一个节点
// 若LinkedList的大小为0,则返回null
public E peek()
if (size == 0)
return null;
return getFirst();
(三)LinkedList能不能作为栈使用?
栈的基本方法
//定义栈
LinkedList<Integer> stack = new LinkedList<Integer>();
//将元素插入到栈顶
stack.push(1)
//取出栈顶的元素并删除栈顶的元素
stack.pop()
//获取栈顶元素,不删除
stack.peek()
演示:
源码分析如下:
.push() //将元素插入到栈顶
// 将e插入到双向链表开头
public void push(E e)
addFirst(e);
.pop() //取出栈顶的元素并删除栈顶的元素
// 删除并返回第一个节点
public E pop()
return removeFirst();
.peek() //获取栈顶元素,不删除
// 返回第一个节点
// 若LinkedList的大小为0,则返回null
public E peekFirst()
if (size == 0)
return null;
return getFirst();
(四)LinkedList作为双端队列使用
双端队列的基本方法
//定义
LinkedList<Integer> deque = new LinkedList<Integer>();
deque.addFirst(); //在队列头部添加
deque.pollFirst(); //删除头部第一个元素(等价于poll())
deque.peekFirst(); //获取头部第一个元素(等价于peek())
deque.addLast(1); //在队列尾部添加(等价于add())
deque.pollLast(); //删除尾部第一个元素
deque.peekLast(); //获取尾部第一个元素
演示:
.addFirst() ; //在队列头部添加
// 将元素添加到LinkedList的起始位置
public void addFirst(E e)
addBefore(e, header.next);
.pollFirst() ; //删除头部第一个元素(等价于poll())
// 删除并返回第一个节点
// 若LinkedList的大小为0,则返回null
public E pollFirst()
if (size == 0)
return null;
return removeFirst();
.peekFirst() ; //获取头部第一个元素(等价于peek())
// 返回第一个节点
// 若LinkedList的大小为0,则返回null
public E peekFirst()
if (size == 0)
return null;
return getFirst();
.addLast() ; //在队列尾部添加(等价于add())
// 将元素添加到LinkedList的结束位置
public void addLast(E e)
addBefore(e, header);
.pollLast() ; // 删除并返回最后一个节点
// 删除并返回最后一个节点
// 若LinkedList的大小为0,则返回null
public E pollLast()
if (size == 0)
return null;
return removeLast();
.peekLast() ; //获取尾部第一个元素
// 返回最后一个节点
// 若LinkedList的大小为0,则返回null
public E peekLast()
if (size == 0)
return null;
return getLast();
总结
底层数据结构是链表,查询慢,增删快,线程不安全,效率高,可以存储重复元素
课后问题:
为什么LinkedList是线程不安全的?
2.ArrayList
package com.example.demo;
import java.util.List;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Vector;
public class working
public static void main(String[] args)
//使用ArrayList类创建列表
List<Integer> numbers = new ArrayList<>(); // <里面不能使用基本类型> 这个在泛型的时候 会说到 要用包装类
//将元素添加到列表
numbers.add(1);
numbers.add(2);
numbers.add(3);
System.out.println("List: " + numbers);
//从列表中访问元素
int number = numbers.get(2);
System.out.println("访问元素: " + number);
//从列表中删除元素
int removedNumber = numbers.remove(1);
System.out.println("删除元素: " + removedNumber);
//将列表转为数组
Object[] array = numbers.toArray();
System.out.print("剩余数组元素为:");
for(int i=0;i<array.length;i++)
System.out.print(array[i]+" ");
//换行
System.out.println();
//从列表中查询某数
System.out.println("能不能从数组中查询到1 "+numbers.contains(1));
(一)ArrayList的底层是数组,数组的名称是什么?类型是什么?
名称是elementData,类型是Object[],所以ArrayList里面可以存放任意类型的元素。
(二)扩容机制
我们都知道Arraylist是一个动态的数组(说白了 也就是 可以根据内容来确定数组的大小)
ArrayList在JDK1.8与JDK1.7底层区别
JDK1.7:ArrayList像 饿汉式 ,直接创建一个初始容量为10的数组,当数组的长度不能容下所添加的内容时候,数组会扩容至原大小的1.5倍
JDK1.8:ArrayList像
懒汉式,一开始创建一个长度为0的数组,当添加第一个元素时再创建一个始容量为10的数组,当数组的长度不能容下所添加的内容时候,数组会扩容至原大小的1.5倍
(三)ArrayList里面可以存null吗?
可以,ArrayList存储的类型是object,null属于object类型。
private void printList()
List<Integer> dataList = new ArrayList<>();
dataList.add(1);
dataList.add(null);
dataList.add(null);
for (Integer d : dataList)
System.out.println(d);
System.out.println("------------------------");
for (Integer d : dataList)
if (d != null) // 需要这个判断吗?
System.out.println(d);
输出:
1
null
null
------------------------
1
(四)ArrayList的底层是数组,它和数组有什么区别吗?
Array 可以包含基本类型和对象类型,ArrayList 只能包含对象类型。
Array 大小是固定的,ArrayList 的大小是动态变化的。
ArrayList 提供了更多的方法和特性,比如:addAll(),removeAll(),iterator()等等。对于基本类型数据,集合使用自动装箱来减少编码工作量。但是,当处理固定大小的基本数据类型的时候,这种方式相对比较慢。
总结
底层数据结构是数组,查询快,增删慢,线程不安全,效率高,可以存储重复元素
课后问题: 为什么说ArrayList是线程不安全的?
面试常见问题
(一)ArrayList与Vector的区别?
(二)ArrayList与LinkedList的区别有哪些?
(三)在调用ArrayList的remove(int index)方法时,执行流程是怎样的?
(四)在ArrayList的增、删、改、查中,什么地方会修改modCount?
(五)ArrayList的时间复杂度是多少?
答案在以前写的Arraylist源码分析中:https://blog.csdn.net/qq_54729417/article/details/121066332
Queue
实现队列的类
由于Queue是一个接口,因此我们无法提供它的直接实现。
为了使用Queue的功能,我们需要使用实现它的类:
- ArrayDeque
- LinkedList
- PriorityQueue
队列数据结构的工作流程
在队列中,以先进先出的方式存储和访问元素。也就是说,从后面添加元素,从前面删除元素。
Queue的方法
Queue接口的一些常用方法是:
- add() - 将指定的元素插入队列。如果任务成功,则add()返回true,否则将引发异常。
- offer() - 将指定的元素插入队列。如果任务成功,则offer()返回true,否则返回false。
- element() - 返回队列的开头。如果队列为空,则引发异常。
- peek() - 返回队列的开头。 如果队列为空,则返回null。
- remove() - 返回并删除队列的头部。如果队列为空,则引发异常。
- poll() - 返回并删除队列的开头。 如果队列为空,则返回null。
相信大家都看到了 这里很多方法都和相似 那么 它们之间有什么区别呢?
offer,add区别:
一些队列有大小限制,因此如果想在一个满的队列中加入一个新项,多出的项就会被拒绝。
这时新的 offer 方法就可以起作用了。它不是对调用 add() 方法抛出一个 unchecked 异常,而只是得到由 offer()
返回的 false。
poll,remove区别:
remove() 和 poll() 方法都是从队列中删除第一个元素(head)。remove() 的行为与 Collection接口的版本相似,
但是新的 poll() 方法在用空集合调用时不是抛出异常,只是返回 null。因此新的方法更适合容易出现异常条件的情况。
peek,element区别:
element() 和 peek() 用于在队列的头部查询元素。与 remove() 方法类似,在队列为空时, element()
抛出一个异常,而 peek() 返回 null。
Set
如何使用set?
在Java中,必须导入java.util.Set包才能使用Set。
//使用HashSet实现Set
Set<String> animals = new HashSet<>();
set方法
Set接口中还提供了Collection接口的一些常用方法:
- add() - 将指定的元素添加到集合中
- addAll() - 将指定集合的所有元素添加到集合中
- iterator() -返回一个迭代器,该迭代器可用于顺序访问集合中的元素
- remove() - 从集合中移除指定的元素
- removeAll() - 从存在于另一个指定集合中的集合中删除所有元素
- keepAll() -保留集合中所有还存在于另一个指定集合中的所有元素
- clear() - 从集合中删除所有元素
- size() - 返回集合的长度(元素数)
- toArray() - 返回包含集合中所有元素的数组
- contains() - 如果集合包含指定的元素,则返回true
- containsAll() - 如果集合包含指定集合的所有元素,则返回true
- hashCode() -返回哈希码值(集合中元素的地址)
Set集合运算
Java Set接口允许我们执行基本的数学集合运算,例如并集,交集和子集。
- Union - 为了得到两个集合x和y的并集,我们可以使用x.addAll(y)
- Intersection - 要获得两个集合x和y的交集,我们可以使用x.retainAll(y)
- Subset - 要检查x是否是y的子集,我们可以使用y.containsAll(x)
Set接口的实现
1.HashSet
package com.example.demo;
import java.util.*;
public class working
public以上是关于Java集合详解的主要内容,如果未能解决你的问题,请参考以下文章