集合概述&集合之List接口
集合与数组存储概述
集合、数组都是对多个数据进行存储操作的结构,简称Java容器。此时的存储,主要指的是内存层面的存储,不涉及到持久化的存储(.txt,.jpg,.avi,数据库中)
数组存储的特点:
- 数组存储数据的特点:有序、可重复。对于无序、不可重复的需求,不能满足。
数组存储的弊端:
一旦初始化以后,其长度就不可修改,不便于扩展。
数组一旦定义好,其元素的类型也就确定了。我们也就只能操作指定类型的数据了。
数组中提供的方法非常有限,对于添加、删除、插入数据等操作,非常不便,同时效率不高。
解决数组存储数据方面的弊端。
集合框架概述
Java 集合可分为 Collection 和 Map 两种体系
Collection接口:单列数据,定义了存取一组对象的方法的集合List:元素有序、可重复的集合
ArrayList、LinkedList、VectorSet:元素无序、不可重复的集合
HashSet、LinkedHashSet、TreeSet
Map接口:双列数据,保存具有映射关系key-value键值对的集合
Collection接口
Collection 接口是 List、Set 和 Queue 接口的父接口,该接口里定义的方法既可用于操作 Set 集合,也可用于操作 List 和 Queue 集合。
JDK不提供此接口的任何直接实现,而是提供更具体的子接口(如:Set和List)实现。
在 Java5 之前,Java 集合会丢失容器中所有对象的数据类型,把所有对象都当成 Object 类型处理;从 JDK 5.0 增加了泛型以后,Java 集合可以记住容器中对象的数据类型

List实现类之一:ArrayList
该类是实现了List的接口,实现了可变大小的数组,随机访问和遍历元素时,提供更好的性能。该类也是非同步的,在多线程的情况下不要使用。ArrayList 增长当前长度的50%,插入删除需要移动后续元素,效率低。
import java.util.ArrayList; // 引入 ArrayList 类
ArrayList<E> objectName =new ArrayList<>(); // 初始化
| 方法 | 描述 |
|---|---|
| add() | 将元素插入到指定位置的 arraylist 中 |
| addAll() | 添加集合中的所有元素到 arraylist 中 |
| clear() | 删除 arraylist 中的所有元素 |
| clone() | 复制一份 arraylist |
| contains() | 判断元素是否在 arraylist |
| get() | 通过索引值获取 arraylist 中的元素 |
| indexOf() | 返回 arraylist 中元素的索引值 |
| removeAll() | 删除存在于指定集合中的 arraylist 里的所有元素 |
| remove() | 删除 arraylist 里的单个元素 |
| size() | 返回 arraylist 里元素数量 |
| isEmpty() | 判断 arraylist 是否为空 |
| subList() | 截取部分 arraylist 的元素 |
| set() | 替换 arraylist 中指定索引的元素 |
| sort() | 对 arraylist 元素进行排序 |
| toArray() | 将 arraylist 转换为数组 |
| toString() | 将 arraylist 转换为字符串 |
| ensureCapacity() | 设置指定容量大小的 arraylist |
| lastIndexOf() | 返回指定元素在 arraylist 中最后一次出现的位置 |
| retainAll() | 保留 arraylist 中在指定集合中也存在的那些元素 |
| containsAll() | 查看 arraylist 是否包含指定集合中的所有元素 |
| trimToSize() | 将 arraylist 中的容量调整为数组中的元素个数 |
| removeRange() | 删除 arraylist 中指定索引之间存在的元素 |
| replaceAll() | 将给定的操作内容替换掉数组中每一个元素 |
| removeIf() | 删除所有满足特定条件的 arraylist 元素 |
| forEach() | 遍历 arraylist 中每一个元素并执行特定操作 |
List实现类之二:LinkedList
前言
链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的地址。
链表可分为单向链表和双向链表。
一个单向链表包含两个值: 当前节点的值和一个指向下一个节点的链接。

一个双向链表有三个整数值: 数值、向后的节点链接、向前的节点链接。

Java LinkedList(链表) 类似于 ArrayList,是一种常用的数据容器。
与 ArrayList 相比,LinkedList 的增加和删除对操作效率更高,而查找和修改的操作效率较低。对于频繁的插入或删除元素的操作,建议使用LinkedList类。
概述
LinkedList:双向链表,内部没有声明数组,而是定义了Node类型的first和last,用于记录首末元素。同时,定义内部类Node,作为LinkedList中保存数据的基本结构。Node除了保存数据,还定义了两个变量:
- prev变量记录前一个元素的位置
- next变量记录下一个元素的位置
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
常用方法
| 方法 | 描述 |
|---|---|
| public boolean add(E e) | 链表末尾添加元素,返回是否成功,成功为 true,失败为 false。 |
| public void add(int index, E element) | 向指定位置插入元素。 |
| public boolean addAll(Collection c) | 将一个集合的所有元素添加到链表后面,返回是否成功,成功为 true,失败为 false。 |
| public boolean addAll(int index, Collection c) | 将一个集合的所有元素添加到链表的指定位置后面,返回是否成功,成功为 true,失败为 false。 |
| public void addFirst(E e) | 元素添加到头部。 |
| public void addLast(E e) | 元素添加到尾部。 |
| public boolean offer(E e) | 向链表末尾添加元素,返回是否成功,成功为 true,失败为 false。 |
| public boolean offerFirst(E e) | 头部插入元素,返回是否成功,成功为 true,失败为 false。 |
| public boolean offerLast(E e) | 尾部插入元素,返回是否成功,成功为 true,失败为 false。 |
| public void clear() | 清空链表。 |
| public E removeFirst() | 删除并返回第一个元素。 |
| public E removeLast() | 删除并返回最后一个元素。 |
| public boolean remove(Object o) | 删除某一元素,返回是否成功,成功为 true,失败为 false。 |
| public E remove(int index) | 删除指定位置的元素。 |
| public E poll() | 删除并返回第一个元素。 |
| public E remove() | 删除并返回第一个元素。 |
| public boolean contains(Object o) | 判断是否含有某一元素。 |
| public E get(int index) | 返回指定位置的元素。 |
| public E getFirst() | 返回第一个元素。 |
| public E getLast() | 返回最后一个元素。 |
| public int indexOf(Object o) | 查找指定元素从前往后第一次出现的索引。 |
| public int lastIndexOf(Object o) | 查找指定元素最后一次出现的索引。 |
| public E peek() | 返回第一个元素。 |
| public E element() | 返回第一个元素。 |
| public E peekFirst() | 返回头部元素。 |
| public E peekLast() | 返回尾部元素。 |
| public E set(int index, E element) | 设置指定位置的元素。 |
| public Object clone() | 克隆该列表。 |
| public Iterator descendingIterator() | 返回倒序迭代器。 |
| public int size() | 返回链表元素个数。 |
| public ListIterator listIterator(int index) | 返回从指定位置开始到末尾的迭代器。 |
| public Object[] toArray() | 返回一个由链表元素组成的数组。 |
| public T[] toArray(T[] a) | 返回一个由链表元素转换类型而成的数组。 |
Set接口
存储的数据特点:无序的、不可重复的元素,且既有数组也有链表
- 无序性:不等于随机性。存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值决定的。
- 不可重复性:保证添加的元素照equals()判断时,不能返回true.即:相同的元素只能添加一个。
Set接口是Collection的子接口,set接口没有提供额外的方法。Set 集合不允许包含相同的元素,如果试把两个相同的元素加入同一个Set 集合中,则添加操作失败。Set 判断两个对象是否相同不是使用 == 运算符,而是根据 equals() 方法
常用实现类
- Set接口:存储无序的、不可重复的数据 -->类似于高中讲的“集合”
HashSet:作为Set接口的主要实现类;线程不安全的;可以存储null值LinkedHashSet:作为HashSet的子类;遍历其内部数据时,可以照添加的顺序遍历。在添加数据的同时,每个据还维护了两个引用,记录此数据前一个数据和后一个数据。对于频繁的遍历操作,LinkedHashSet效率高于HashSet.
TreeSet:可以照添加对象的指定属性,进行排序。
元素添加过程:(以HashSet为例)
向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算出元素a的哈希值
此哈希值接着通过某种散列函数计算出在HashSet底层数组中的存放位置(即索引位置),然后判断此位置上是否已经元素:
- 如果此位置上没其他元素,则元素a添加成功。 --->情况1
如果此位置上有其他元素b,则比较元素a与元素b的hash值,若有多个元素则挨个比较并以链表形式存储
- 如果hash值不相同,则元素a添加成功。--->情况2
- 如果hash值相同,进而需要调用元素a所在类的equals()方法:
- equals()返回true表明该元素已存在,元素a添加失败;equals()返回false,则元素a添加成功。-->情况3
对于添加成功的情况2和情况3而言:元素a 与已经存在指定索引位置上数据以链表的方式存储。
jdk 7 :元素a指向原来的元素。
jdk 8 :原来的元素指向元素a
总结:七上八下
注意
- 向Set(主要指:HashSet、LinkedHashSet)中添加的数据,其所在的类一定要重写
hashCode()和equals() - 重写的
hashCode()和equals()尽可能保持一致性:相等的对象必须具有相等的散列码
结论:重写equals方法的时候一般都需要同时重写hashCode方法。通常参与计算hashCode 的对象的属性也应该参与到equals() 中进行计算
Set实现类之一:HashSet
- HashSet 基于 HashMap 来实现的,是一个不允许有重复元素的集合。
- HashSet 允许有 null 值。
- HashSet 是无序的,即不会记录插入的顺序。
- HashSet 不是线程安全的, 如果多个线程尝试同时修改 HashSet,则最终结果是不确定的。 您必须在多线程访问时显式同步对 HashSet 的并发访问。
- HashSet 实现了 Set 接口。
- HashSet 中的元素实际上是对象,一些常见的基本类型可以使用它的包装类
| 变量和类型 | 方法 | 描述 |
|---|---|---|
boolean |
add(E e) |
如果指定的元素尚不存在,则将其添加到此集合中。 |
void |
clear() |
从该集中删除所有元素。 |
Object |
clone() |
返回此 HashSet实例的浅表副本:未克隆元素本身。 |
boolean |
contains(Object o) |
如果此set包含指定的元素,则返回 true 。 |
boolean |
isEmpty() |
如果此集合不包含任何元素,则返回 true 。 |
Iterator<E> |
iterator() |
返回此set中元素的迭代器。 |
boolean |
remove(Object o) |
如果存在,则从该集合中移除指定的元素。 |
int |
size() |
返回此集合中的元素数(基数)。 |
Spliterator<E> |
spliterator() |
在此集合中的元素上创建late-binding和失败快速 Spliterator |
Set实现类之二:LinkedHashSet
LinkedHashSet 是 HashSet 的子类,LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置,但它同时使用双向链表维护元素的次序,这使得元素看起来是以插入顺序保存的。
LinkedHashSet插入性能略低于 HashSet,但在频繁的地遍历操作时有很好的性能。LinkedHashSet的存在就是为了频繁地遍历
Set实现类之三:TreeSet的使用
TreeSet和后面要讲的TreeMap,采用红黑树的存储结构
特点:有序,查询速度比List快
TreeSet 两种排序方法:自然排序和定制排序。默认情况下,TreeSet 采用自然排序
自然排序中,比较两个对象是否相同的标准为:重写的compareTo()返回0,不再是equals()
如果试图把一个对象添加到 TreeSet 时,则该对象的类必须实现
Comparable接口。向 TreeSet 中添加元素时,只有第一个元素无须比较compareTo()方法,后面添加的所有元素都会调用compareTo()方法进行比较。
因为只有相同类的两个实例才会比较大小,所以向 TreeSet 中添加的应该是同
一个类的对象。
重写该对象对应的 equals() 方法时,应保证该方法与 compareTo(Object obj) 方法有一致的结果
定制排序中,比较两个对象是否相同的标准为:compare()返回0,不再是equals()。TreeSet的自然排序要求元素所属的类实现Comparable接口,如果元素所属的类没有实现Comparable接口,或不希望按照升序(默认情况)的方式排列元素或希望按照其它属性大小进行排序,则考虑使用定制排序。
利用int compare(T o1,T o2)方法,比较o1和o2的大小:如果方法返回正整数,则表
示o1大于o2;如果返回0,表示相等;返回负整数,表示o1小于o2。
要实现定制排序,需要将实现Comparator接口的实例作为形参传递给TreeSet的构
造器
仍然只能向TreeSet中添加类型相同的对象。否则发生
ClassCastException异常
//方式一:自然排序
@Test
public void test1(){
TreeSet set = new TreeSet();
//失败:不能添加不同类的对象
// set.add(123);
// set.add(456);
// set.add("AA");
// set.add(new User("Tom",12));
//举例一:
// set.add(34);
// set.add(-34);
// set.add(43);
// set.add(11);
// set.add(8);
//举例二:
set.add(new User("Tom",12));
set.add(new User("Jerry",32));
set.add(new User("Jim",2));
set.add(new User("Mike",65));
set.add(new User("Jack",33));
set.add(new User("Jack",56));
Iterator iterator = set.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
//方式二:定制排序
@Test
public void test2(){
Comparator com = new Comparator() {
//照年龄从小到大排列
@Override
public int compare(Object o1, Object o2) {
if(o1 instanceof User && o2 instanceof User){
User u1 = (User)o1;
User u2 = (User)o2;
return Integer.compare(u1.getAge(),u2.getAge());
}else{
throw new RuntimeException("输入的数据类型不匹配");
}
}
};
TreeSet set = new TreeSet(com);
set.add(new User("Tom",12));
set.add(new User("Jerry",32));
set.add(new User("Jim",2));
set.add(new User("Mike",65));
set.add(new User("Mary",33));
set.add(new User("Jack",33));
set.add(new User("Jack",56));
Iterator iterator = set.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
遍历Collection的两种方式
Iterator迭代器
- java.utils包下定义的迭代器接口:Iterator
- Iterator对象称为迭代器(设计模式的一种),主要用于遍历 Collection 集合中的元素。
- GOF给迭代器模式的定义为:提供一种方法访问一个容器(container)对象中各个元素,而又不需暴露该对象的内部细节。迭代器模式,就是为容器而生。
Iterator iter = coll.iterator();//回到起点
//hasNext():判断是否还有下一个元素
while(iter.hasNext()){
//next():①指针下移 ②获取指针所指位置的元素
Object obj = iter.next();
if(obj.equals("Tom")){
iter.remove();
}
}
Iterator接口中的remove()方法
- 可在遍历的时候删除集合中的元素
- 不同于集合直接调用remove(),collection返回的是boolean型,即是否删除成功
- 如果未调用next()或已经调用过remove(),再调用remove()则会报错IllegalStateException
foreach增强for循环
- Java 5.0 提供了 foreach 循环迭代访问 Collection和数组。
- 遍历操作不需获取Collection或数组的长度,无需使用索引访问元素。
- 遍历集合的底层调用Iterator完成操作。
- foreach还可以用来遍历数组。
面试题
@Test
public void testListRemove() {
List list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
updateList(list);
System.out.println(list);// [1,2]
}
private static void updateList(List list) {
list.remove(2); //基本类型不会自动封装,当成索引
list.romove(new Integer(2)); //[1,3]
}
//练习:在List内去除重复数字值,要求尽量简单
public static List duplicateList(List list) {
HashSet set = new HashSet();
set.addAll(list);
return new ArrayList(set);
}
public static void main(String[] args) {
List list = new ArrayList();
list.add(new Integer(1));
list.add(new Integer(2));
list.add(new Integer(2));
list.add(new Integer(4));
list.add(new Integer(4));
List list2 = duplicateList(list);
for (Object integer : list2) {
System.out.println(integer);
}
}
集合概述&集合之List接口的更多相关文章
- Java精选笔记_集合概述(Collection接口、Collections工具类、Arrays工具类)
集合概述 集合有时又称为容器,简单地说,它是一个对象,能将具有相同性质的多个元素汇聚成一个整体.集合被用于存储.获取.操纵和传输聚合的数据. 使用集合的技巧 看到Array就是数组结构,有角标,查询速 ...
- Day07_34_集合概述
集合概述 * 主要集合概述 - 集合相当于现实世界中的容器,主要包含两种存放模式,一个一个的存(Collection), 一对一对存(Map[key,value]) - 集合中只能存储引用数据类型,不 ...
- Collection集合概述和集合框架介绍
集合概述 集合:集合是java中提供的一种容器,可以用来存储多个数据 集合和数组既然都是容器,他们有什么区别? 1.数组的长度是固定的,集合的长度是可变的 2.数组中存储的是同一类型的元素,可以存储基 ...
- 包装类总结-Collection集合概述
包装类总结 1.基本数据类型对应的包装类byte Byteshort Shortint Integerlong Longfloat Floatdouble Doublechar Characterbo ...
- Java集合----概述、Collection接口、Iterator接口
Java 集合概述 Java 集合就像一种容器,可以把多个对象的引用放入容器中. Java 集合类可以用于存储数量不等的多个对象,还可用于保存具有映射关系的关联数组 Java 集合可分为 Set.Li ...
- 《java集合概述》
JAVA集合概述: Collection: |---List有序的:通过索引就可以精确的操作集合中的元素.元素是可以重复的. List提供了增删改查的动作. 增加add(element) add(in ...
- Java集合概述、Set集合(HashSet类、LinkedHashSet类、TreeSet类、EnumSet类)
Java集合概述.Set集合(HashSet类.LinkedHashSet类.TreeSet类.EnumSet类) 1.Java集合概述1)数组可以保存多个对象,但数组长度不可变,一旦在初始化数组时指 ...
- Java笔记:Java集合概述和Set集合
本文主要是Java集合的概述和Set集合 1.Java集合概述 1)数组可以保存多个对象,但数组长度不可变,一旦在初始化数组时指定了数组长度,这个数组长度就是不可变的,如果需要保存数量变化的数据,数组 ...
- Map集合概述和特点
A:Map集合概述和特点(Set底层依赖的是Map) 将键映射到值的对象 一个映射不能包含重复的键 每个键最多只能映射到一个值 B:Map接口和Collection接口的不同 Map是双列的(是双列集 ...
随机推荐
- python编码问题:UnicodeDecodeError: 'gbk' codec can't decode byte 0xaf in position 68: illegal multibyte sequence
import yaml def test_yaml(): f = open('C:\hogwarts\Scripts\hogwarts-api\demo\yaml_data.yml') print(y ...
- K8s一键安装
安装案例: 系统:Centos可以多台Master(Master不能低于3台)多台Node此案例使用三台Master两台Node,用户名root,密码均为123456 master 192.168.2 ...
- RabbitMQ持久化机制、内存磁盘控制(四)
一.持久化 如果看到这一篇文章的朋友,都是有经验的开发人员,对持久化的概念就不用再做过多的解析了,经过前面的几篇文章,其实不难发现RabbitMQ 的持久化其实就只分交换器持久化.队列持久化和消息持久 ...
- 网页常用的css特效让互动留住客户
一般网站如果制作按钮,多做一些互动,可以让客户获得更好的体验. 例如鼠标滑过按钮,让背景颜色从左往右滑出来(或者从右往左都可以): <a target="_blank" hr ...
- Convolutional Neural Network-week2编程题1(Keras tutorial - 笑脸识别)
本次我们将: 学习到一个高级的神经网络的框架,能够运行在包括TensorFlow和CNTK的几个较低级别的框架之上的框架. 看看如何在几个小时内建立一个深入的学习算法. 为什么我们要使用Keras框架 ...
- HTML中的emment
emment的优点和使用方法: 优点:Emment是前端开发者必备的工具,使用它可以大大提高前端开发效率. 使用方法:Emment的使用方法非常简单,直接在编辑器上输入HTML和CSS代码的缩写,然后 ...
- JVM:类加载与字节码技术-2
JVM:类加载与字节码技术-2 说明:这是看了 bilibili 上 黑马程序员 的课程 JVM完整教程 后做的笔记 内容 这部分内容在上一篇笔记中: 类文件结构 字节码指令 编译期处理 类加载阶段 ...
- 占位符,SQL注入?
这两天在上课时被同学拿了一段代码问我,这段代码有什么问题,我看了一会说:Connection和PreparedStatement都没关.他说不止这方面的问题,还有sql注入的问题,我就坚决的说使用了占 ...
- UltraSoft - Alpha - Scrum Meeting 6
Date: Apr 21th, 2020. Scrum 情况汇报 进度情况 组员 负责 昨日进度 后两日任务 CookieLau PM 验证了课程中心获取课程资源和作业的爬虫方式 细化前后端交互中的难 ...
- Noip模拟10 2021.6.27
T1 入阵曲 好了,又一个考试败笔题. 也就是在那个时候,小 F 学会了矩阵乘法.让两个矩阵乘几次就能算出斐波那契数, 真是奇妙无比呢. 不过, 小 F 现在可不想手算矩阵乘法--他觉得好麻烦.取而代 ...