【Java 集合】ArrayList、LinkedList、Stack、Queue、Set、Map, 迭代器 Iterable、Iterator,Collections类
集合类型
- 数组的局限性
- ArrayList(常用方法 + 基本使用)
- ArrayList — retainAll
- ArrayList — toArray
- ArrayList 的遍历(5种)
- ArrayList 的扩容原理
- 自定义迭代器 Iterable、Iterator
- 集合遍历的注意点(遍历集合的同时删除元素)
- ListIterator(遍历同时对元素进行操作)
- LinkedList
- LinkedList vs ArrayList
- Stack
- Queue
- Set
- HashSet
- LinkedHashSet
- TreeSet
- Map
- HashMap
- HashMap — 遍历
- LinkedHashMap
- TreeMap
- List vs Set vs Map
- java.util.Collections
Java笔记目录可以点这里:Java 强化笔记(适合有基础的童鞋,不适合小白)
这篇文章只是学习一些集合的使用,更多底层细节可以看这个 【恋上数据结构】数据结构与算法
java.util
包中有个集合框架(Collections Framework),提供了一大堆常用的数据结构
ArrayList
、LinkedList
、Queue
、Stack
、HashSet
、HashMap
等
数据结构是计算机存储、组织数据的方式:
- 在实际应用中,根据使场景来选择最合适的数据结构
集合框架预览:
下图:紫色代表接口、蓝色是抽象类、绿色是实现类。
数组的局限性
数组的局限性:
- 无法动态扩容
- 操作元素的过程不够面向对象
- …
ArrayList
是 Java 中的动态数组:
- 一个可以动态扩容的数组
- 封装了各种实用的数组操作
ArrayList(常用方法 + 基本使用)
常用方法:
public int size() // 元素数量
public boolean isEmpty() // 是否为空
public boolean contains(Object o) // 是否包含元素o
public int indexOf(Object o) // 从前往后查询元素o的下标
public int lastIndexOf(Object o) // 从后往前查询元素o的下标
public E get(int index) // 获取下标为index的元素
public E set(int index,E element) // 设置下标index的元素为element
public boolean add(E e) // 往最后添加一个元素e
public void add(int index,E element) // 往下标index处添加一个元素e
public E remove(int index) // 移除index处的元素
public boolean remove(Object o) // 移除元素o
public void clear() // 清空public Object[] toArray() // 转为数组
public <T>T[] toArray(T[] a) // 转为数组(指定类型)
public void trimToSize() // 缩容, 缩小到当前容量
public void ensureCapacity(int minCapacity) // 扩容public boolean addAll(Collection<? extends E>c)
public boolean addAll(int index, Collection<? extends E> c)
public boolean removeAll(Collection<?>c)
public boolean retainAll(Collection<?>c)
public void forEach(Consumer<? super E> action)
public void sort(Comparator<? super E> c)
ArrayList
的基本使用:
// 如果不使用泛型, ArrayList中可以放任何东西, 不推荐这样使用(不安全)
ArrayList list = new ArrayList();
list.add(11);
list.add(false);
list.add(null);
list.add(3.14);
list.add(0, "jack");
list.add('8');// 3
System.out.println(list.indexOf(null));
// 6
System.out.println(list.size());
// [jack, 11, false, null, 3.14, 8]
System.out.println(list);
ArrayList — retainAll
list1.retainAll(list2)
: 会从 list1
中删除掉 list2
中元素以外的所有元素。
List<Integer> list1 = new ArrayList<>();
list1.add(11);
list1.add(22);
list1.add(33);
list1.add(44);List<Integer> list2 = new ArrayList<>();
list2.add(22);
list2.add(44);// 从 list1 中删除掉 list2 中元素以外的所有元素
list1.retainAll(list2);
// [22, 44]
System.out.println(list1);
需要注意的是 public boolean retainAll(Collection<?>c)
接收的参数类型是 Collection
,Java 中大部分集合之间都可以互相调用 retainAll
。
List<Integer> list = new ArrayList<>();
list1.add(11);
list1.add(22);
list1.add(33);
list1.add(44);Stack<Integer> stack = new Stack<>();
stack.push(11);// 从 list1 中删除掉 stack 中元素以外的所有元素
list.retainAll(stack);
// [11]
System.out.println(list);
ArrayList — toArray
Integer[] array0 = (Integer[]) list.toArray();
这段代码是不可行的,原因是 ArrayList
中不写泛型是可以任意添加元素的,强转 list.toArray()
的返回值极其不安全,会抛出 java.lang.ClassCastException
异常。
List<Integer> list = new ArrayList<>();
list.add(11);
list.add(22);
list.add(33);// 不可以强转为Intger[], 会抛出异常: java.lang.ClassCastException
// Integer[] array0 = (Integer[]) list.toArray();// 可以利用 Object[]来接收, 但是更推荐下面的写法
Object[] array1 = list.toArray();
// [Ljava.lang.Object;@15db9742
System.out.println(array1);
// [11, 22, 33]
System.out.println(Arrays.toString(array1));// 比起用 Object[]接收,更推荐这种写法
Integer[] array2 = list.toArray(new Integer[0]);
// [Ljava.lang.Integer;@6d06d69c
System.out.println(array2);
// [11, 22, 33]
System.out.println(Arrays.toString(array2));
具体为什么不能转,请对比下面一系列情况:
Object obj1 = 11;
// 可以正常转换
Integer obj2 = (Integer) obj1;
System.out.println(obj2); // 11
Object obj1 = new Object();
// java.lang.ClassCastException
Integer obj2 = (Integer) obj1;
System.out.println(obj2);
Object[] array1 = {11, 22, 33};
// 上一行代码的本质就是下面一行
// Object[] array1 = new Object[] {11, 22, 33};
// java.lang.ClassCastException
Integer[] array2 = (Integer[]) array1;
System.out.println(array1);
ArrayList 的遍历(5种)
经典的 for 循环遍历,可以获取遍历元素的下标:
// 经典
int size = list.size();
for (int i = 0; i < size; i++) {System.out.println(list.get(i));
}
利用迭代器进行遍历:
// 迭代器
Iterator<Object> it = list.iterator();
while(it.hasNext()) {Object obj = it.next();System.out.println(obj);
}
实现了 Iterable
接口的对象,都可以使用 for-each 格式遍历元素:比如 List
、Set
等;
Iterable
在使用 for-each 格式遍历元素时,本质是使用了 Iterator
对象(迭代器)。
// for-each 格式
for (Object obj : list) {System.out.println(obj);
}
集合的 forEach
利用匿名内部类遍历:
list.forEach(new Consumer<Object>() {@Overridepublic void accept(Object t) {System.out.println();}
});
利用了 lambda 表达式的 forEach
:
list.forEach((obj) -> {System.out.println(obj);
});
最精简的遍历输出,了解一下即可:
list.forEach(System.out::println);
ArrayList 的扩容原理
具体的在 恋上数据结构(第一季) 已经讲得很详细了,我已烂熟于心…再不济点进源码看看就行。
自定义迭代器 Iterable、Iterator
前面说过,实现了 Iterable
接口的对象,都可以使用迭代器和 for-each 格式(本质还是迭代器)遍历元素。
如何让自定义对象实现实现可迭代的功能呢?
import java.util.Iterator;public class ClassRoom implements Iterable<String> {private String[] students;public ClassRoom(String... students) { // 可变参数的本质是数组 this.students = students; // 所以可变参数可以用数组来接收}@Overridepublic Iterator<String> iterator() {return new ClassRoomIterator();}private class ClassRoomIterator implements Iterator<String> {private int index; // 游标@Overridepublic boolean hasNext() {return index < students.length;}@Overridepublic String next() {return students[index++];}}}
我们来测试一下它的遍历:
ClassRoom room = new ClassRoom("Jack", "Rose", "Jerry");
// for-each格式遍历
for (String string : room) {System.out.println(string);
}
// 迭代器遍历
Iterator<String> it = room.iterator();
while(it.hasNext()) {System.out.println(it.next());
}
集合遍历的注意点(遍历集合的同时删除元素)
首先我们先往集合里加点东西:
List<Integer> list = new ArrayList<>();
list.add(11);
list.add(22);
list.add(33);
list.add(44);
然后我们通过遍历的方式,挨个删除所有的元素。
可能有人会这么写,这样是错误的:抛出异常 java.lang.IndexOutOfBoundsException
int size = list.size();
for (int i = 0; i < size; i++) {// java.lang.IndexOutOfBoundsExceptionlist.remove(i);
}
或者会这么写,这样写也是错误的:会少删除元素
for(int i =0; i < list.size(); i++) {list.remove(i);
}
System.out.println(list); // [22, 44]
下面两种写法也是错误的:抛出异常 java.util.ConcurrentModificationException
// java.util.ConcurrentModificationException
list.forEach((e) -> {list.remove(e);
});
// java.util.ConcurrentModificationException
for (Integer e : list) {list.remove(e);
}
我们会发现,遍历的集合的方法特别多,但是如果要在遍历的同时删除元素,以上都是不可行的。
使用迭代器、forEach
遍历集合元素时,若使用了集合自带的方法修改集合的长度(比如 add
、remove
等方法),会抛出 java.util.ConcurrentModificationException
异常。
如果希望在遍历元素的同时删除元素:
- 请使用
Iterator
进行遍历 - 然后使用
Iterator
的remove
方法删除元素
Iterator<Integer> it = list.iterator();
while(it.hasNext()) {it.next();it.remove();
}
// 删除成功
System.out.println(list); // []
System.out.println(list.size()); // 0
ListIterator(遍历同时对元素进行操作)
ListIterator
继承自 Iterator
,在 Iterator
的基础上增加了一些功能
boolean hasNext(); // 是否有下一个元素
E next(); // 获取下一个元素
boolean hasPrevious(); // 是否有前一个元素
E previous(); // 获取前一个元素
int nextIndex();
int previousIndex();
void remove();
void set(E e);
void add(Ee);
基本使用:
List<Integer> list = new ArrayList<>();
list.add(11);
list.add(22);
list.add(33);ListIterator<Integer> it = list.listIterator();
// 从前往后遍历
while (it.hasNext()) { System.out.println(it.next());
} // 11 22 33// 从后往前遍历
while (it.hasPrevious()) {System.out.println(it.previous());
} // 33 22 11
遍历同时对元素进行操作:
ListIterator<Integer> it = list.listIterator();
while (it.hasNext()) {it.set(it.next() + 55);
}
System.out.println(list); // [66, 77, 88]
遍历同时添加元素:
ListIterator<Integer> it = list.listIterator();
while(it.hasNext()) {it.add(66);System.out.println(it.next());it.add(77);
}
System.out.println(list); // [66, 11, 77, 66, 22, 77, 66, 33, 77]
LinkedList
LinkedList
是一个双向链表
- 实现了 List 接口
- API 跟 ArrayList 类似
LinkedList vs ArrayList
Stack
Stack
常用方法:
Stack
使用:
Stack<Integer> stack = new Stack<>();
stack.push(11);
stack.push(22);
stack.push(33);System.out.println(stack.peek()); // 33System.out.println(stack.search(11)); // 3while(!stack.isEmpty()) {System.out.println(stack.pop());
} // 33 22 11
Queue
Queue
常用方法:
Queue
使用:
Queue<Integer> queue = new LinkedList<>();
queue.add(11);
queue.add(22);
queue.add(33);System.out.println(queue.element()); // 11while (!queue.isEmpty()) {System.out.println(queue.remove());
} // 11 22 33
Set
- 只要是
Set
都可以用作去重。
HashSet
HashSet
简单使用:
Set<String>set = new HashSet<>();
set.add("Jack");
set.add("Rose");
set.add("Kate");
set.add("Jack");
set.add("Hello");System.out.println(set.size()); // 4// 元素的存储是无序的
System.out.println(set); // [Kate, Hello, Rose, Jack]set.remove("B");
System.out.println(set); // [Kate, Hello, Rose, Jack]
HashSet
的遍历:
Set<String> set = new HashSet<>();
set.add("Jack");
set.add("Rose");
set.add("Kate");// 元素的遍历是无序的// 增强for循环遍历
for (String str : set) {System.out.println(str);
} // Kate Rose jack// 迭代器遍历
Iterator<String> it = set.iterator();
while (it.hasNext()) {System.out.println(it.next());
} // Kate Rose jack// forEach遍历
set.forEach((str) -> {System.out.println(str);
}); // Kate Rose jack
LinkedHashSet
LinkedHashSet
在HashSet
的基础上(可去重),记录了元素的添加顺序
LinkedHashSet
简单使用:
Set<String> set = new LinkedHashSet<>();
set.add("Jack");
set.add("Rose");
set.add("Kate");
set.add("Jack");
set.add("Hello");System.out.println(set.size()); // 4// 元素的存储是有序的(添加顺序)
System.out.println(set); // [Jack, Rose, Kate, Hello]// 元素的遍历是有序的(添加顺序)
for (String str : set) {System.out.println(str); // Jack Rose Kate
}set.remove("B");
System.out.println(set); // [Jack, Rose, Kate, Hello]
TreeSet
TreeSet
要求元素必须具备可比较性,默认从小到大的顺序遍历元素(可通过比较器修改)
// 字符串继承了Comparable, 具有可比较性, 默认按字母顺序从小到大
Set<String> set =new TreeSet<>();
set.add("Jack");
set.add("Rose");
set.add("Jim");
set.add("Kate");
set.add("Rose");
set.add("Larry");System.out.println(set); // [Jack, Jim, Kate, Larry, Rose]for (String str : set) {System.out.println(str); // Jack Jim Kate Larry Rose
}
通过比较器自定义比较方式:
// 默认比较方式是从小到大
// 通过比较器修改为从大到小
Set<Integer> set = new TreeSet<>((i1, i2) -> i2 -i1);
set.add(33);
set.add(11);
set.add(55);
set.add(22);
set.add(44);System.out.println(set); // [55, 44, 33, 22, 11]for (Integer i : set) {System.out.println(i); // 55 44 33 22 11
}
Map
HashMap
HashMap
存储的是键值对(key-value),Map 译为 映射,有些编程语言中叫做 字典
HashMap
简单使用:
HashMap<String, Integer> map = new HashMap<>();
map.put("Jack", 11);
map.put("Rose", 22);
map.put("Jim", 22);
map.put("Jack", 33); // 重复
map.put("Kate", 11);System.out.println(map.size()); // 4System.out.println(map.get("Jack")); // 33System.out.println(map); // {Kate=11, Rose=22, Jack=33, Jim=22}map.remove("Rose");
System.out.println(map); // {Kate=11, Jack=33, Jim=22}
HashMap — 遍历
HashMap<String, Integer> map = new HashMap<>();
map.put("Jack", 11);
map.put("Rose", 22);
map.put("Jim", 33);
最简洁的遍历方法:通过 forEach
遍历
// forEach 遍历, 推荐
map.forEach((key, value) -> {System.out.println(key + "=" + value);
}); // Rose=22 Jack=11 Jim=33
通过键值对 entrySet
遍历:
// entrySet 是一组键值对, 通过键值对来遍历, 推荐
Set<Entry<String, Integer>> entries = map.entrySet();
for (Entry<String, Integer> entry : entries) {System.out.println(entry.getKey() + "=" + entry.getValue());
} // Rose=22 Jack=11 Jim=33
先取出 value 的集合,再遍历:无法通过 value 获取 key,只能遍历 value
// 只能遍历 value
Collection<Integer> values = map.values(); // value的集合
for (Integer value : values) {System.out.println(value);
} // 22 11 33
取出 key 的集合,再遍历:可以通过 key 获取 value,但是效率低下
Set<String> keys = map.keySet(); // key的集合
for (String key : keys) {// 先遍历key, 又通过key去找到value (整体效率较低)System.out.println(key + "=" + map.get(key));
} // Rose=22 Jack=11 Jim=33
LinkedHashMap
LinkedHashMap
在HashMap
的基础上,记录了元素的添加顺序
Map<String, Integer> map = new LinkedHashMap<>();
map.put("Jack", 11);
map.put("Rose", 22);
map.put("Jim", 33);
map.put("Kate", 44);System.out.println(map); // {Jack=11, Rose=22, Jim=33, Kate=44}map.forEach((k, v) -> {System.out.println(k + "=" + v);
}); // Jack=11 Rose=22 Jim=33 Kate=44map.remove("Rose");
System.out.println(map); // {Jack=11, Jim=33, Kate=44}
TreeMap
TreeMap
要求 key 必须具备可比较性,默认从小到大的顺序遍历 key(可通过比较器修改)
Map<String, Integer> map = new TreeMap<>();
map.put("Jack", 11);
map.put("Rose", 22);
map.put("Jim", 33);
map.put("Kate", 44);
map.put("Larry", 55);System.out.println(map); // {Jack=11, Jim=33, Kate=44, Larry=55, Rose=22}map.forEach((k, v) -> {System.out.println(k + "=" + v);
}); // Jack=11 Jim=33 Kate=44 Larry=55 Rose=22
List vs Set vs Map
java.util.Collections
java.util.Collections
是一个常用的集合工具类,提供了很多实用的静态方法
【Java 集合】ArrayList、LinkedList、Stack、Queue、Set、Map, 迭代器 Iterable、Iterator,Collections类相关推荐
- 【腾讯面试题】Java集合:List、Set以及Map
Java集合:List.Set以及Map 概述 Collection接口 List:有序,可重复 ArraysList Vector LinkedList Set:无序,唯一 HashSet Link ...
- java集合-ArrayList
java集合 ArrayList Iterable Comparable(排序接口) 项目结构: class Dog implements Comparable<Dog> {private ...
- List 系列 ArrayList LinkedList CopyOnWriteArrayList Queue系列 ArrayDeque ConcurrentLinkedDeque LinkedBlo
LinkedBlockingDeque作为一种阻塞双端队列,提供了队尾删除元素和队首插入元素的阻塞方法.该类在构造时一般需要指定容量,如果不指定,则最大容量为Integer.MAX_VALUE.另外, ...
- Java集合框架总结(5)——Map接口的使用
Java集合框架总结(5)--Map接口的使用 Map用于保存具有映射关系的数据(key-vlaue).Map的key不允许重复,即同一个Map对象的任何两个key通过equals方法比较总是返回fa ...
- Java集合框架--List、Set、Map
Java集合框架接口和类的层次结构: java.util.Collection [I] +--java.util.List [I]+--java.util.ArrayList [C]+--java.u ...
- Java集合的使用:List与Map
由David发表在天码营 Java的容器 定义好Post类之后,现在需要对博客文章进行管理.我们可以定义一个PostRepository类,通过PostRepository可以做以下操作: 创建博客 ...
- Java集合 ArrayList 扩容
Java的集合框架 集合的分类 可以分为单列集合和双列集合 单列集合主要有Collection下面的List与Set,其中List最长被用到的是ArrayList与LinkedList以及Vctor三 ...
- java vector arraylist linkedlist用法与区别
首先,它们是list的实现类,大致说一下vector arraylist linkedlist的区别. 1.线程安全来讲, vector是线程安全,arraylist linkedlist线程不安全. ...
- java集合框架LinkedList类的方法理解
java提供的LinkedList类是双向列表,列表中的每个节点都包含了对前一个和后一个元素的引用. LinkedList与ArrayList一样实现List接口,只是ArrayList是List接口 ...
- java集合-ArrayList学习笔记
集合和数组关系? 1) 集合是一种容器,可以存储多个数据. 2) 数组的长度是固定的,集合的长度是可变的. 3) 集合中存储的元素必须为引用类型数据,初始值为NULL Arra ...
最新文章
- linux 压缩 解压 命令大全
- xp系统如何开启索引服务器,Windows XP系统关闭磁盘索引的两个方法图文教程
- 如何快速掌握一门新技术/语言/框架…
- [基础]iOS 可视化编程(全系列)
- Linux中常见目录的作用
- 寒假作业3:抓老鼠啊
- Head First Python 学习笔记(第二章:分享你的代码)
- asp fso的神奇功能
- GIF图形文件格式文档
- 【视频开发】【Live555】摄像头采集,264编码,live555直播
- step14. ubuntu18.04下载安装scala(转)
- CODING 助力江苏高速信息实现组织敏捷与研发敏捷,领跑智慧交通新基建
- 如何在水平线中间添加文字
- Spring boot项目 maven多模块打包,子模块报错问题解决
- 计算机切换器鼠标反应慢,kvm切换器故障操作解决方法详解
- mysql直方图_MySQL · 特性分析 · 直方图的实现与分析
- Ardupilot移植经验分享(2)
- 只需Ctrl+T,让 Excel 变身为「超级表格」
- MySQL数据库的官网下载、安装及卸载(2018年最新)
- img 图片 下边距 解决办法
热门文章
- 抖音打破了传统互联网的商业逻辑
- 家庭网络,怎么给每个房间装一个无线路由器?
- 苹果电脑如何修改环境变量
- sql server序列_SQL Server中的Microsoft时间序列
- alwayson高可用组_AlwaysOn可用性组–如何在集群实例和独立实例之间设置AG(第2部分)
- 如何在SQL Server中检查日期是否为假期
- Spring注解@ConfigurationPropertie
- HTML(XHTML)基础知识(三)——【image】
- js进阶 10-9 -of-type型子元素伪类选择器
- 【转】C++中的inline用法