集合
数组 与 集合(容器)的区别 :
共同点 :
存储多个数据
不同点 :
数组 :
1.引用数据类型,是对象数据
2.定长,长度一旦确定不可改变
3.只能存储多个类型相同的数据
4.有序的,有索引
优势:
是一种简单的线性序列,可以快速的访问数组元素,效率高。如果从 效率和类型检查的角度讲,数组是最好的。
劣势:
不灵活:长度一旦定义,不可改变 容量事先定义好,不能随着需求的变化而扩容。
集合 :
1.引用数据类型,是对象数据
2.集合长度的跟随数据的增删进行改动
3.可以任意引用数据类型的数据,存储的多个数据类型可以不同
4.有些集合有序,有些集合无序,有些可以根据索引进行操作,有些不 可以
无论是何种容器使用步骤和方式都类似,首先需要有一个容器对象,然后通过方法对容器中的数据进行增 删 改 查操作。
Collection 接口:
规定了一些方法,即作为一个容器就应该具有这些功能。在Collection 中并没有任何的可以存储数据的地方,因此只是作为一个规范存在。
Set
概念:
Set extends Collection 没有顺序,不可重复:
可以存放很多数据,但是set中的数据不能重复 如果前后数据重复,只存放一个
Set接口下有一个 HashSet 实现类,HashSet的底层是用 HashMap 实现的( 看了HashMap后再回来看HashSet的实现 ),因此,查询效率高。由于采用Hashcode算法直接确定元素的内存地址,增删效率也高。
HashSet 接口中的元素,无序不可重复,不包含重复元素,最多包含一个 null,元素没有顺序 。
元素的存储顺序,不由存放顺序而定,容器有自己的一套存放顺序的规则。
Set<String> set = new HashSet<>();
set.add("AAAA");
set.add("CCCC");
set.add("EEEE");
set.add("DDDD");
set.add("BBBB");
for (String s : set) {
System.out.println(s);
}
===================================================================
EEEE
AAAA
CCCC
DDDD
BBBB
TreeSet
底层是由TreeMap维护的 无序的,不可重的
底层结构 : 红黑树(平衡二叉树)
特点 : 查询效率高,去重,默认升序排序
引用场景 : 适合应用在存储多个单个值的数据的集合,去重的,自动升序排序的场景
新增方法 : 新增了一些与比较大小相关的方法
遍历方式 : 1)foreach 2)iterator
public class Class002_TreeSet {
public static void main(String[] args) {
TreeSet<Double> set = new TreeSet<>();
set.add(5.2);
set.add(3.2);
set.add(1.2);
set.add(2.2);
set.add(4.2);
System.out.println(set);
set.add(4.2);
System.out.println(set);
//课后练习 : 测试使用TreeSet存储字符数据,存储字符串数据
//E ceiling(E e) 返回此set中大于或等于给定元素的最小数据,如果没有这样的元素,则 null 。
System.out.println(set.ceiling(2.2)); //2.2
//E floor(E e) 返回此set中小于或等于给定元素的最大元素,如果没有这样的元素,则 null 。
System.out.println(set.floor(3.1));
//E first() 返回此集合中当前的第一个(最低)元素。
System.out.println(set.first());
//E last() 返回此集合中当前的最后一个(最高)元素。
System.out.println(set.last());
//E higher(E e) 返回此集合中的最小元素严格大于给定元素,如果没有这样的元素,则 null 。
System.out.println(set.higher(2.2)); //3.2
//E lower(E e) 返回此集合中的最大元素严格小于给定元素,如果没有这样的元素,则 null 。
//E pollFirst() 检索并删除第一个(最低)元素,如果此组为空,则返回 null 。
//E pollLast() 检索并删除最后一个(最高)元素,如果此集合为空,则返回 null 。
}
}
1.测试TreeSet是否能够同时存储不同类型的数据 : java.lang.ClassCastException 不能存储多个不同类型数据,因为不是同种类型数据无法比较大小,无法升序排序
2.定义TreeSet存储自定义User类型的数据 : java.lang.ClassCastException
查到TreeSet集合中存储的类型数据的比较规则
1)定义TreeSet时候,构造器中通过参数传递比较规则–>外部比较规则
2)检查存储的数据类型是否存在默认的比较规则,如果存在使用,如果还不存在,就抛出异常 -->内部比较规则
比较器|比较规则 :
内部比较器|内部比较规则|自然排序 : 实体类实现java.lang.Comparable接口,重写int compareTo(T o) 方法,方法的内部定义比较规则
int compareTo(T o) : x.compareTo(y)
返回值 : 比较的结果
0 --> x==y
<0 --> x<y
>0 --> x>y
外部比较器|外部比较规则|定值排序 : 实现类实现Comparator接口重写compare方法,方法内部定义比较规则
注意 : TreeSet集合存储自定义的引用数据类型数据,去重与升序排序都是根据比较规则(比较器) 需要自己实现内部或外部比较器,根据实际情况选择
HashSet
无序的,不可重复的
底层是由HashMap维护
底层结构 : 哈希表(jdk1.8之前 : 数组+链表 jdk1.8及之后 : 数组+链表+红黑树)
特点 : 查询增删效率较高
应用场景 : 去重的需求下,查询增删相对效率较高的集合类型适合使用HashSet
遍历方式 : 1)foreach 2)iterator
去重 : 要求重写哈希表存储的数据类型的hashCode与equals方法,实现根据所有属性值重写,而非根据地址比较计算
重写 hashCode与equals方法的前提下 : 根据所有属性值重写
equals返回值相等两个对象,调用hashCode肯定相等(相等才能在同一个桶中,才有机会调用equals比较去重)
hashCode返回值相等,equals不一定相等
public class Class005_HashSet {
public static void main(String[] args) {
HashSet<Person> set = new HashSet<>();
set.add(new Person("张三",18));
set.add(new Person("张三",18));
set.add(new Person("张三",18));
set.add(new Person("张三",18));
//集合业务: 想要去重根据对象的所有属性值进行去重,而非根据地址
System.out.println(set);
System.out.println(new Person("张三",18).hashCode());
System.out.println(new Person("张三",18).hashCode());
}
}
class Person{
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
遍历:
普通for循环, 用不了,因为Set集合的元素是没有下标的,不可以通过下标获取
增强for:
for(String s : set){
System.out.println(s);
}
迭代器:
Iterator<String> it = set.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
集合中存放自定义类型的数据
List
概念:
List extends Collection有顺序 ,可以重复:
此接口的用户可以对列表中每个元素的插入位置进行精确地控制。用户 可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。同样是一个接口也只是 提供了一些抽象方法,做出相应的规范。
Collection中的集合,元素是孤立存在的(理解为单身),向集合中存储元素采用一个个元素的方 式存储。
可以存放很多数据,List中的数据可以重复
获取 get 查询对象出现的索引 indexOf(Object)
List接口下有两个实现类 LinkedList 和 ArrayList ,其中都实现了List中的方法,并且都给出了可以 存储数据的空间。
使用方法:
//void add( Object element);
//void add (int index, Object element);
//Object get (int index);
//Object set (int index,Object element);//修改某一位置的元素
//Object remove (int index);
//int indexOf (Object o);//返回某个元素的索引。如果没有该数据,返回-1
List<String> list = new LinkedList<>();
list.add("a");
list.add("b");
list.add(1,"good");
list.add(0,"nice");
list.add("hello");
System.out.println(list.size());
list.remove("a");
System.out.println(list.size());
list.set(0,"first");
System.out.println(list.get(0));
list.clear();
System.out.println(list.size())
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eGbbcMl3-1678365234936)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]
ArrayList
有空间才有可以储存数据的地方
有序,可重复,可以根据索引进行操作
List接口的可调整大小的阵列实现。 实现所有可选列表操作,并允许所有元素,包括null 。这个类大致相当于Vector ,除了它是不同步的。
底层结构 : 可变数组 Object[] elementData;
特点 : 根据索引查询效率高,做增删效率低,因为大量的涉及到新数组创建,数据的拷贝
引用场景 : 适合应用在大量做查询,少量做增删的场景
初始容量 : 10 DEFAULT_CAPACITY = 10
扩容机制 : 通过Arrays.copyOf方法实现创建新数组拷贝数据,每次扩容原容量的1.5倍 int newCapacity = oldCapacity + (oldCapacity >> 1);
遍历方式 : 1.for 2.foreach 3.Iterator 4.ListIterator
注意 : 如果某个类型底层结构为数组或数组相关类型,需要关注初始容量与扩容机制
Object[] elementData; // 用来存放元素的地方
int size; // 记录容器中当前元素个数
int DEFAULT_CAPACITY = 10; // 默认第一次开辟空间的大小
Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; //空集合的空间
实现方法
构造器,无参构造的话,创建的容器对象,是没有任何空间可以存放数据
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
默认添加元素
判断是否还有空间可以添加新的元素,如果没有那么进行扩容【1、如果已经有元素,空间增大并且将原来的值,拷贝到新的数组中;2、如果还没有元素,只需要有空间即可】
添加元素【添加元素到末尾,size 增大】
private void add(E e, Object[] elementData, int s) {
// 检查是否有足够的空间,是否需要扩容
if (s == elementData.length){
// 扩容
elementData = grow();
}
// 添加元素到数组里边最后一个元素后面
elementData[s] = e;
// 元素个数 增大1个
size = s + 1;
}
在指定的位置添加元素
检查添加元素指定的位置是否合法,判断是否还有空间添加新的元素,如果没有空间就扩容
添加的时候,挪动,将要添加的位置及其后所有的元素都往后移动个,将新的元素添加到指定位置即可,size增大
public void add(int index, E element) {
// 检查位置是否合法
rangeCheckForAdd(index);
modCount++;
final int s;
Object[] elementData;
// 判断是否有足够的空间
if ((s = size) == (elementData =this.elementData).length)
// 扩容
elementData = grow();
// 挪动位置
System.arraycopy(elementData, index,
elementData, index + 1,
s - index);
// 添加新元素到指定的位置
elementData[index] = element;
// 增大容器中元素的 个数
size = s + 1;
}
获取指定位置上的元素
检查指定的下标是否合法,如果下标合法,返回数组中指定位置的元素
public E get(int index) {
// 检查下标是否合法
Objects.checkIndex(index, size);
return elementData(index);
}
删除指定位置的元素
检查删除的元素指定的位置是否合法
通过下标获取要删除的元素
快删【检查是否删除的最后一个元素,如果不是,挪动要删的元素位置之后所有元素到前一位】,将数组中最后一个元素的值设为null
最后需要将元素的个数-1
public E remove(int i) {
// 检查元素的下标是否和法
Objects.checkIndex(i, size);
final Object[] es = elementData;
final int newSize;
// 查看是否删除最后一个元素
if ((newSize = size - 1) > i){
// 如果不是删除最后一个元素,挪动要删元素的位置之后的所有元素向前1个
// 挪动之后删除的就是最后一个元素
System.arraycopy(es, i + 1, es, i, newSize - i);
// 将最后一个元素的值设为null,删除了最后一个元素
}else[size = newSize] = null;
return oldValue;
}
删除指定的元素
判断要删除的元素是否为null,如果为null,在容器中【数组中】查找null,找到之后保存下标,进行按照位置删除。如果要删除的元素不为null,在容器中【数组中】查找,equals进行比较,当比较到为true,认为找到了要删除的元素,保存下标,按照位置进行删除。
public boolean remove(Object o) {
final Object[] es = elementData;
final int size = this.size;
int i = 0;
found: {
// 判断要删除的元素是否为空null
if (o == null) {
for (; i < size; i++)
// 在数组中查找到null ==
if (es[i] == null)
// 结束整个查找
break found;
} else {
// 如果要删除的元素不为null
for (; i < size; i++)
// 就是用 equals进行判定
if (o.equals(es[i]))
break found;
}
// 没有找到要删的元素,false表示没删任何东西
return false;
}
// 已经找到了要删除的元素的下标,则按照下标去删除这个元素
fastRemove(es, i);
return true;
}
清空所有元素【容器】
将容器中的size,记录元素个数的值【属性】设为0
将数组中的每一个元素值都设为null
public void clear() {
modCount++;
final Object[] es = elementData;
// 将记录size置为0
for (int to = size, i = size = 0; i < to; i++)
// 将元素值设为null
es[i] = null;
}
查看容器是否为空 其个数是否为0
public boolean isEmpty() {
// 查看size的值是否为0
return size == 0;
}
查看容器中元素的个数 查看size值即可
public int size() {
return size;
}
LinkedList
有序的,可重复的
实现所有可选列表操作,并允许所有元素(包括null )。
底层结构 : 双向链表
特点 : 查询效率低,增删效率高
应用场景: 适合应用在大量做增删,少量做查询的情况
新增方法 : 新增了一些操作链表头尾的方法
遍历方式 : 1)for 2)foreach 3)iterator 4)listIterator
双向链表实现:
有属性才能够储存数据
int size = 0; // 记录容器中元素的个数
Node<E> first; // 记录链表的头部,记录第一个元素的位置
Node<E> last; // 记录的是链表的尾部,记录最后一个元素的位置
Note节点(内部类)
E item; // 数据
Node<E> next; // 指向下一个元素
Node<E> prev; // 指向前一个元素
创建容器对象
public LinkedList() {
}
fist = null;
last = null;
向集合中添加元素,默认添加到末尾
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
临时存储了last指向的对象的地址
利用需要添加的数据,以及创建了一个Node对象newNode
l-> prev
e->item
null -> next
将新创建出来的对象给到last
判断l是否为null,其实就是判断原来的last是否为null,本质上就是在判断集合中原来是否有元素
如果l为null-> 添加的是第一个元素,再将newNode的地址给到first
如果l不为null->添加的不是第一个元素,则将newNode的地址给到原来的最后一个节点的next属性size++,更新元素个数
添加元素到指定的位置
首先检查下标是否合法
如果是添加到最后的位置,直接利用刚才方法即可
如果添加到不是最后的位置,利用给定的下标,查找要添加元素到哪一个元素之前
先定义一个临时变量得到第一个节点(元素)
一直往后移动index个元素
将要添加的目的地元素返回回去(我要添加到这个元素之前)
删除指定的元素(按照元素内容)
1)、首先根据要删除的元素值去找到要删除的节点,根据要删除的元素的值是否为null
null: o == x.item
!null: o.equals(x.item)
2)、开始删除节点
先备份要删除的节点的内容(item, prev, next)
(对prev的操作)判断要删除的节点是否是第一个节点
如果要删除的节点是第一个节点(prevnull)
将first重新赋值 要删除的节点的下一个节点
如果要删除的节点不是第一个节点(prev!=null)
将要删除的节点的下一个节点的地址,给到要删除的节点的 前一个节点的next属性中,相当于将要删除的节点的前一个节点和后一个节点建立了连接,将要删除的节点的prev属性置为null
(对next的操作)判断要删除的节点是否是最后一个节点
如果要删除的节点是最后一个节点(nextnull)
将last重新赋值 要删除的节点的前一个节点
如果要删除的节点不是最后一个节点(next!=null)
将要删除的节点的上一个节点的地址,给到要删除的节点的后一个节点的prev属性中,相当于将要删除的节点的后一个节点和前一个节点建立了练习,将要删除的节点的next属性置为null
最后再将要删除的节点的item置为null
size–
删除指定位置上的元素
检查要删除的位置是否合法
根据位置(下标)找要删除的元素(节点)
unlinke删除指定的节点即可
查看容器中元素的个数
查看容器是否为空
清空容器
将所有的元素的所有属性都置为null
first, last 设置为null
size = 0
Vector向量
ArrayList与Vector区别 :
共同点 : 都是List接口的实现类,有序可重复的
底层结构都是数组,查询效率高,增删效率低
不同点 : 线程安全|同步问题 :
ArrayList线程不安全|不同步
Vector 线程安全|同步
如果不需要线程安全实现,建议使用ArrayList代替Vector 。 初始容量与扩容机制 :
ArrayList 初始容量默认10|可以在构造器中指定,在add方法中创建
Vector 初始容量默认10|可以在构造器中指定,在构造器中直接创建 ArrayList 每次扩容原容量的1.5倍
int newCapacity = oldCapacity + (oldCapacity>>1); Vector 默认每次扩容原容量的2倍|可以根据需求指定每次扩容多少-->容量增量
int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity);
List遍历:
普通for循环 ,利用List元素有下标的特点,依次获取每一个元素
for (int i=0; i<list.size(); i++){
System.out.println(list.get(i));
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yqY1l056-1678365234937)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]
增强for 依次获取集合中每一个元素存入一个临时变量中
for (String temp : list) {
System.out.println(temp);
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CN4buZ8p-1678365234937)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]
底层用双向链表实现的List。特点:查询效率低,增删效率高,线程不安全。
底层用数组实现的List。特点:查询效率高,增删效率低,线程不安全。
迭代器(Iterator)
所有实现了Collection接口的容器类都有一个 iterator 方法用以返回一个实现了Iterator接口的对象。
Iterator对象称作迭代器,用以方便的实现对容器内元素的遍历操作。
Iterator接口定义了如下方法:
boolean hasNext(); //判断是否有元素没有被遍历 有没有下一个元素
Object next(); //返回游标当前位置的元素并将游标移动到下一个位置
void remove(); //删除游标左面的元素,在执行完next之后该 ,操作只能执行一次
Iterator<String> it = list.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
Set与List之间的区别:
List: 有序的,元素可以重复
判断集合中是否包含一个指定对象的时候,是通过对象的 equals 方法进行比较的
Set: 无序的,元素是不重复的
添加元素之前,就已经经过了判断,判断当前集合中是否已经存在待添加的元素
钻石运算符 | 泛型 :
jdk5新特性
为什么要使用泛型?
1.避免警告标记
2.解决一下2种情况 :
1)编译期间无法对集合中存储数据的类型进行检查,可以实现存储任意引用数据类型的数据
2)获取的时候想要对应类型的引用接收,直接显示做强制类型转换,就有可能遇到类型转换异常
什么是泛型 :
将数据类型作为参数传递
在编译期间提供了类型检查机制,在编译期间允许程序检测不合法的类型数据 --> 泛型擦除
如何使用泛型 :
<数据类型> : 注意为引用数据类型
jdk1.6及之前版本 :
泛型前后<>必须统一传递数据类型
jdk1.7.8允许 :
后面类型<数据类型>可以省略为<>
在集合泛型使用时,经常会遇到几种通配符 : E T K V ?
? : 不确定的java类型
T(Type) : 某一种确定Java类型
E(Element) : 元素的类型
K(Key) : java中键值对的键的类型
V(Value) : java中键值对的值的类型
public class Class003_Type {
public static void main(String[] args) {
List<String> col = new ArrayList<String>();
col.add("zhangsan");
col.add("李四");
col.add("王五");
//1.6及之前
ArrayList<String> list = new ArrayList<String>();
//1.7
ArrayList<Integer> list2 = new ArrayList<>();
for(String str:col){
System.out.println(str);
}
Iterator<String> it = col.iterator();
while(it.hasNext()){
String s = it.next();
System.out.println(s);
}
}
}
Map 接口:
概念:
Map中的集合,元素是成对存在的(理解为夫妻)。每个元素由键与值两部分组成,通过键可以找对所对 应的值。
Map也是容器的一种,但是没有继承Collection接口,以键值对的形式存放数据,一个key对应一个value。key和value都是引用数据类型,我们可以通过key找到对应的value。
Map类中存储的键-值对通过 键 来标识,所以 键值不能重复 。
Map中的集合不能包含重复的键,值可以重复; 每个键只能对应一个值。
Object put(Object key, Object value);
Object get(Object key);
Object remove(Object key);
boolean containsKey(Object key);
boolean containsValue(Object value);
int size();
boolean isEmpty();
void putAll(Map t);
void clear();
常见的实现类有 HashMap 、 HashTable 等
注意:如果存入的键值对中key相同,那么后面的会覆盖掉前面的当数据存放到Map之后,我们是无法控制存放数据的顺序的
TreeMap
存储键值对数据,key是唯一的,去重的,无序的(存放的顺序与内部真实存储的顺序不保证一致)
底层结构 : 红黑树
特点 : 根据key做自动升序排序(比较器)
去重与升序排序 : 根据key比较规则做去重与排序
新增方法 : 新增了一些与比较大小相关的方法
注意 : Map集合下所有的实现类,去重|排序都是根据key实现的,与value无关
public class Class003_TreeMap {
public static void main(String[] args) {
//TreeMap<String,Double> map = new TreeMap<>(); 使用key的类型的默认比较器进行对键值对key做比较大小,做升序排序
//TreeMap(Comparator<? super K> comparator) 构造一个新的空树图,根据给定的比较器排序。
//TreeMap<Person,Double> map = new TreeMap<>(new Impl()); //根据参数传递的比较规则对key做比较去重与排序
//匿名内部类
/*TreeMap<Person,Double> map = new TreeMap<>(new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge()-o2.getAge();
}
});*/
//lambda
TreeMap<Person,Double> map = new TreeMap<>((o1, o2)->{
return o1.getAge()-o2.getAge();
});
map.put(new Person("zhangsan",18),10000.0);
map.put(new Person("wangwu",27),8888.0);
map.put(new Person("lisi",26),9999.0);
map.put(new Person("zhangsan",28),12000.0);
System.out.println(map);
//Map.Entry<K,V> ceilingEntry(K key) 返回与大于或等于给定键的最小键关联的键 - 值映射,如果没有此键,则 null 。
//K ceilingKey(K key) 返回大于或等于给定键的 null键,如果没有这样的键,则 null 。
System.out.println(map.ceilingKey(new Person("lisi",27)));
System.out.println(map.get(map.ceilingKey(new Person("lisi",27))));
//Map.Entry<K,V> floorEntry(K key) 返回与小于或等于给定键的最大键关联的键 - 值映射,如果没有此键,则 null 。
//K floorKey(K key) 返回小于或等于给定键的最大键,如果没有这样的键,则 null 。
System.out.println("--->"+map.floorKey(new Person("lisi",27)));
//Map.Entry<K,V> lowerEntry(K key) 返回与严格小于给定键的最大键相关联的键 - 值映射,如果没有这样的键,则 null 。
//K lowerKey(K key) 返回严格小于给定键的最大键,如果没有这样键,则返回 null 。
System.out.println("--->"+map.lowerKey(new Person("lisi",27)));
//Map.Entry<K,V> firstEntry() 返回与此映射中的最小键关联的键 - 值映射,如果映射为空,则 null 。
//K firstKey() 返回此映射中当前的第一个(最低)键。
System.out.println(map.firstKey());
//Map.Entry<K,V> lastEntry() 返回与此映射中的最大键关联的键 - 值映射,如果映射为空,则 null 。
//K lastKey() 返回此映射中当前的最后一个(最高)键。
}
}
class Impl implements Comparator<Person>{
@Override
public int compare(Person o1, Person o2) {
return o2.getAge()-o1.getAge();
}
}
class Person implements Comparable<Person>{
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public int compareTo(Person o) {
return this.name.compareTo(o.name);
}
}
HashMap:
概念:
基于哈希表的Map接口的实现。 此实现提供了所有可选的映射操作,并允许null值和null键。
底层结构 : 哈希表(jdk8之前:数组+链表 jdk8及之后:数组+链表+红黑树)
特点 : 查询增删效率高,去重根据键值对的key实现
应用场景 : 适合应用在存储多个键值对数据的情况下,查询增删效率高应用场景
遍历方式 : 1)values() 2)keySet() 3)entrySet()
初始容量 :默认static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; --> 可以通过构造器指定
加载因子 : 默认static final float DEFAULT_LOAD_FACTOR = 0.75f; --> 可以通过构造器指定
扩容阈值 : threshold 存储数据的个数size > 数组的容量*加载因子
扩容机制 : 每次扩容原容量的2倍 newCap = oldCap << 1哈希表中存储键值对的个数 : size
HashMap的储存过程:
1.调用put方法添加键值对key,value数据
2.根据key计算hash值
int hash = (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
3.调用putVal(hash,key,value)实现存储键值对数据
4.执行putVal方法,第一步就判断哈希表底层的节点数组是否null,或者底层数组的长度0,如果是证明这是第一次添加,底层没有数组或者没有带有容量的数组,先调用resize()方法实现创建新数组
if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length;
5.根据key的hash值计算位桶索引int index = (n - 1) & hash,判断table[index]==null是否存在单向链表的首节点
if ((p = tab[i = (n - 1) & hash]) == null)
6.如果=null,证明不存在单向链表,直接创建新节点,存储这一次要添加的键值对数据,直接放在数组table[index]作为单向链表的首节点
tab[i] = newNode(hash, key, value, null); --> new Node<>(hash, key, value, next)
7.如果!=null,证明存在单向链表的首节点,遍历这个链表,判断链表中每一个节点的存储的key与这一次要添加的键值对的key比较是否相等,
if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
如果相同value覆盖
V oldValue = e.value;
e.value = value;
return oldValue;
如果不相同创建新节点,挂在原单向链表的最后
p.next = newNode(hash, key, value, null);
8.如果以上的步骤中执行到了new Node()创建新节点放入哈希表中,数据个数+1,判断是否>扩容阈值threshold,如果满足,就调用resize方法实现扩容
if (++size > threshold)
resize();
注意 :
哈希表底层节点数组的长度为2的整数次幂
当单向链表的节点个数>=8(static final int TREEIFY_THRESHOLD = 8;),并且同时哈希表的节点数组的容量>=64(static final int MIN_TREEIFY_CAPACITY = 64;),就把单向链表优化成红黑树
但是如果数组的容量<64,就调用resize()实现数组的扩容
public class Class004_HashMap {
public static void main(String[] args) {
HashMap<String,Character> map = new HashMap<>();
map.put("yinwei",'z');
map.put("laoxue",'w');
map.put("lucy",'u');
map.put("lisa",'a');
System.out.println(map);
map.put("lisa",'s');
System.out.println(map);
map.remove("lisa");
System.out.println(map);
}
}
HashMap作为Map的实现类,其中的方法都进行了实现,并且定义了可以存储数据的空间,其中使用了一个叫做 Entry 的内部类,作为数据的存储
Map<String, String> map = new HashMap<>();
map.put("1","一,壹,Ⅰ");
map.put("2","二,贰,Ⅱ");
map.put("3","三,叁,Ⅲ");
System.out.println(map.get("1"));
HashMap中存储数据的特点:
存储的元素是以K-V的形式存在的
map集合中 key 必须要唯一,如果添加了相同的键值对(键相同)会发生覆盖
map集合中元素(键值对)是无序的,和Set集合类似
HashMap:数组单向链表红黑树实现
HashMap是基于哈希表实现的,每一个元素是一个key-value对,其内部通过单链表解决冲突问题,容量不足(超过了阀值)时,同样会自动增长。
HashMap是非线程安全的,只是用于单线程环境下,多线程环境下可以采用concurrent并发包下的concurrentHashMap。
HashMap 实现了Serializable接口,因此它支持序列化,实现了Cloneable接口,能被克隆。
HashMap存数据的过程是:
HashMap内部维护了一个存储数据的Entry数组,HashMap采用链表解决冲突,每一个Entry本质上是一个单向链表。当准备添加一个key-value对时,首先通过hash(key)方法计算hash值,然后通过indexFor(hash,length)求该key-value对的存储位置,计算方法是先用hash&0x7FFFFFFF后,再对length取模,这就保证每一个key-value对都能存入HashMap中,当计算出的位置相同时,由于存入位置是一个链表,则把这个key-value对插入链表头。
HashMap中key和value都允许为null。key为null的键值对永远都放在以table[0]为头节点的链表中。
HashMap内存储数据的Entry数组默认是16,如果没有对Entry扩容机制的话,当存储的数据一多,Entry内部的链表会很长,这就失去了HashMap的存储意义了。所以HasnMap内部有自己的扩容机制。
HashMap内部有:
变量size,它记录HashMap的底层数组中已用槽的数量。
变量threshold,它是HashMap的阈值,用于判断是否需要调整HashMap的容量(threshold= 容量*加载因子) 。
变量DEFAULT_LOAD_FACTOR = 0.75f,默认加载因子为0.75。
HashMap扩容的条件是:
当size大于threshold时,对HashMap进行扩容 。
扩容是是新建了一个HashMap的底层数组,而后调用transfer方法,将就HashMap的全部元素添加到新的HashMap中(要重新计算元素在新的数组中的索引位置)。 很明显,扩容是一个相当耗时的操作,因为它需要重新计算这些元素在新的数组中的位置并进行复制处理。因此,我们在用HashMap的时,最好能提前预估下HashMap中元素的个数,这样有助于提高HashMap的性能。
HashMap共有四个构造方法:
构造方法中提到了两个很重要的参数:初始容量和加载因子。
这两个参数是影响HashMap性能的重要参数,其中容量表示哈希表中槽的数量(即哈希数组的长度),初始容量是创建哈希表时的容量(从构造函数中可以看出,如果不指明,则默认为16),加载因子是哈希表在其容量自动增加之前可以达到多满的一种尺度,当哈希表中的条目数超出了加载因子与当前容量的乘积时,则要对该哈希表进行 resize 操作(即扩容)。
加载因子:
如果加载因子越大,对空间的利用更充分,但是查找效率会降低(链表长度会越来越长)。
如果加载因子太小,那么表中的数据将过于稀疏(很多空间还没用,就开始扩容了),对空间造成严重浪费。
如果我们在构造方法中不指定,则系统默认加载因子为0.75,这是一个比较理想的值,一般情况下我们是无需修改的。
Map遍历:
1.Collection<V> values() 返回此映射中包含的值的Collection视图。2.Set<K> keySet() 返回此映射中包含的键的Set视图。3.Set<Map.Entry<K,V>> entrySet() 返回此映射中包含的映射的Set视图。
keyset:
将所有的 key 获取存入 Set 集合中,遍历存储 key 的 Set 集合,结合 get(key) 方法可以获取map 中每一个 key 以及对应的 value。
Set<String> keys = map.keySet();
// 增强for
for(String key : keys){
System.out.println(key + " - " + map.getKey(key));
}
// 迭代器
Iterator<String> it = keys.iterator();
while(it.hasNext()){
String key = it.next();
System.out.println(key + " - " + map.getKey(key));
}
values:
将所有的 value 值获取后存入Collection集合中,遍历存储了所有 key 的集合,则可以获取到所有的value 值,但此种方式只能获取到每一个value,并不能获取到key。
Collection<String> values = map.values();
// 增强for
for (String value : values) {
System.out.println(value);
}
// 迭代器
Iterator<String> it = values.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
EntrySet:
将Map集合中的每一个键值对作为一个 Entry 对象获取到一个Set集合中,通过对象的 getKey 和getValue 方法获取 key 和 value
Set<Map.Entry<String, String>> entries = map.entrySet();
// 增强for
for (Map.Entry<String, String> entry : entries) {
System.out.println(entry.getKey() + "-"+entry.getValue());
}
// 迭代器
Iterator<Map.Entry<String, String>> it = entries.iterator();
while(it.hasNext()){
Map.Entry<String, String> entry = it.next();
System.out.println(entry.getKey() + "-" + entry.getValue());
}
无论是List还Set或者是Map都是集合,都是为了存储具有不同特征的数据,根据实际情况选择不同的容器即可。
表象看集合里面数据的特征:
List(ArrayList,LinkedList):单个数据,数据是可以重复的,而且可以指定位置
Set:单个数据,数据是不可以重复,而且是无序的(不由用户来指定位置|顺序)
Map:成键值对的数据,数据是没有顺序的,键是不可以重复的
HashMap与HashTable:
Hashtable和HashMap底层都是哈希表实现的
Hashtable和HashMap的区别:
继承的父类不同:
Hashtable继承自Dictionary类,而HashMap继承自AbstractMap类。但二者都实现了Map接口。
线程安全性不同:
javadoc中关于hashmap的一段描述如下:此实现不是同步的。如果多个线程同时访问一个哈希映射,而其中至少一个线程从结构上修改了该映射,则它必须保持外部同步。
参看StringBuilder和StringBuffer
是否提供contains方法:
HashMap把Hashtable的contains方法去掉了,改成containsValue和containsKey,因为contains方法容易让人引起误解。
Hashtable则保留了contains,containsValue和containsKey三个方法,其中contains和containsValue功能相同。
key和value是否允许null值:
Hashtable中,key和value都不允许出现null值。
HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。当get()方法返回null值时,可能是 HashMap中没有该键,也可能使该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键,而应该用containsKey()方法来判断。
两个遍历方式的内部实现上不同:
Hashtable、HashMap都使用了 Iterator。而由于历史原因,Hashtable还使用了Enumeration的方式 。
hash值不同:
HashTable直接使用对象的hashCode。而HashMap重新计算hash值。
内部实现使用的数组初始化和扩容方式不同:
HashTable在不指定容量的情况下的默认容量为11,而HashMap为16,Hashtable不要求底层数组的容量一定要为2的整数次幂,而HashMap则要求一定为2的整数次幂。
Hashtable扩容时,将容量变为原来的2倍加1,而HashMap扩容时,将容量变为原来的2倍。
Hashtable 与 HashMap之间区别 :
共同点 :
都是Map接口的实现类,都存储键值对的数据,底层都是哈希表,特点与应用场景类似
异同点 :
1.继承体系不同
Hashtable-->父类-->java.util.Dictionary<K,V> HashMap -->父类-->java.util.AbstractMap<K,V>
2.线程安全|不同问题
HashMap : 线程不安全|不同步的,但是效率相对较高 Hashtable : 线程安全|同步,但是效率相对较低
3.null值的处理不同
Hashtable : 任何非null对象都可以用作键或值。 HashMap : 并允许null值和null键。
4.初始容量与扩容机制不同
初始容量 :
Hashtable : 11
HashMap : 16
扩容机制
Hashtable : 每次扩容原容量的2倍+1 int newCapacity = (oldCapacity << 1) + 1;
HashMap : 每次扩容原容量的2倍 newCap = oldCap << 1
5.计算hash值与位桶索引index的方式不同 Hashtable :
int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; HashMap :
int hash = (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
int index = (n - 1) & hash;
容器遍历与删除
阿里巴巴的Java规范中有说到: 不要在foreach循环里进行元素的remove/add操作,remove元素请使用Iterator方式。
如果修改其结构,会抛出异常 ConcurrentModificationException
This exception may be thrown by methods that have detected concurrent modification of an object when such modification is not permissible.
投机技巧
如果你实在想用 增强for循环 遍历删除,那么也只能在删除一个后,立刻退出循环。
循环删除list中特定一个元素的,可以使用三种方式中的任意一种,但在使用中要注意上面分析的各个问题
循环删除list中多个元素的,应该使用迭代器ListIterator方式
切记:能用迭代器就用迭代器
工具类
Properties
Properties为Hashtable的子类,要求键与值只能为字符串 ,不能为null,长与 配置文件(与外界交互 的信息) 即内存与存储介质(文件、数据库、网络、服务器内存等)交互。
原来的集合(Set, List, Map) 都是内存中自己给数据,Peroperties 特殊在于,他自己去读取磁盘中文件的内容,添加到集合中,直接使用有数据的集合即可(自己提供集合对象)。一般使用在配置文件中
Properties pro = new Properties();
pro.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("db.properties")); // 通过当前线程获取ClassLoader
System.out.println(pro.getProperty("userName"));
db.properties配置文件
userName=zhangsan
userPwd=123
/*
Properties :
Properties类表示一组持久的属性
Properties可以保存到流中或从流中加载。
属性列表中的每个键及其对应的值都是一个字符串。
注意 :
应用场景 : 从properties格式的配置文件中读取键值对数据
步骤 :
1.定义xx.properties的配置文件
2.构建Properties类型的对象
3.调用load方法实现从指定流中加载
4.调用getProperty方法根据key获取value从文件中加载数据
*/
public class Class001_Properties {
public static void main(String[] args) throws IOException {
Properties pro = new Properties();
pro.setProperty("嘻嘻","123");
pro.setProperty("哈哈","456");
System.out.println(pro);
//1)通过Properties对象从数据源文件中加载键值对数据(属性数据)
//把pro对象与流建立联系,从流中加载数据
//void load(InputStream inStream) 从输入字节流中读取属性列表(键和元素对)。
//pro.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("classname.properties"));
//根据key获取value,根据属性名获取属性值
//System.out.println(pro.getProperty("xixi"));;
//System.out.println(pro.getProperty("haha"));;
//System.out.println(pro.getProperty("hehe"));;
//2)把Properties存储的键值对数据写出到文件中-->了解
//pro对象与流建立联系,把键值对数据写出到目的地properties文件中
//void store(OutputStream out, String comments)
BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream("src/dest.properties"));
pro.store(os,"注释xixihaha");
os.flush();
os.close();
}
}
Collections
类java.util.Collections提供了对Set、List、Map操作的工具方法
void sort(List) //对List容器内的元素排序,排序的规则是按照升序进行排序。
void shuffle(List) //对List容器内的元素进行随机排列
void reverse(List) //对List容器内的元素进行逆续排列
void fill(List, Object) //用一个特定的对象重写整个List容器
int binarySearch(List, Object)//对于顺序的List容器,采用折半查找的方法查找特定对象
如何处理HashMap线程不安全问题 :
1.Hashtable代替HashMap
2.使用Collections工具类的static <K,V>Map<K,V> synchronizedMap(Map<K,V> m) 返回由指定映射支持的同步(线程安全)映射。
3.juc高级并发编程包ConcurrentHashMap<K,V> --> 推荐
例:
List aList = new ArrayList();
for (int i = 0; i < 5; i++){
aList.add("a" + i);
}
System.out.println(aList);
Collections.shuffle(aList); // 随机排列
System.out.println(aList);
Collections.reverse(aList); // 逆续
System.out.println(aList);
Collections.sort(aList); // 排序
System.out.println(aList);
System.out.println(Collections.binarySearch(aList, "a2"));
Collections.fill(aList, "hello");
System.out.println(aList);
比较器
在Java中经常会涉及到对象数组|集合的排序问题,那么就涉及到对象之间的比较问题。
对于JDK8而言,有三种实现对象比较的方法:
1、覆写Object类的equals()方法;
2、继承Comparable接口,并重写compareTo()方法;
3、定义一个单独的对象比较器,继承自Comparator接口,重写compare()方法。
由于使用的排序方式的不同,具体选择哪种方法来实现对象的比较也会有所不同。
我们下面讨论的就是排序中使用到的比较器Comparable和Comparator。
Comparable和Comparator都是java包下的两个接口,从字面上看这两个接口都是用来做比较用的,但是jdk里面不可能定义两个功能相同的接口,所以他们肯定有不同的用处。
Comparable和Comparator接口都是为了对类进行比较,众所周知,诸如Integer,double等基本数据类型,java可以对他们进行比较,而对于类的比较,需要人工定义比较用到的字段比较逻辑。
Comparable接口
内部比较器,实现了Comparable接口的类 可以和自己比较
若一个类实现了 java.lang.Comparable 接口, Comparable 接口中只有一个方法 public int compareTo(Object obj) 就意味着该类支持排序。
返回 0 表示 this == obj ;
返回正数表示 this > obj ;
返回负数表示 this < obj 。
实现了Comparable接口的类的对象的列表或数组可以通过Collections.sort或Arrays.sort进行自动排序。
实现此接口的对象可以用作有序映射中的键或有序集合中的集合,无需指定比较器。
实现了Comparable 接口的类通过实现 comparaTo 方法从而确定该类对象的排序方式。
public class TestComparable {
public static void main(String[] args) {
List<Student> list = new ArrayList<Student>();
Student stu1 = new Student(1,"张三",100);
Student stu2 = new Student(2,"张四",80);
Student stu3 = new Student(3,"张五",90);
list.add(stu1);
list.add(stu2);
list.add(stu3);
System.out.println(list);
Collections.sort(list);
System.out.println(list);
}
}
class Student implements Comparable<Student> {
int id;
String name;
int score;
public Student(int id, String name, int score) {
super();
this.id = id;
this.name = name;
this.score = score;
}
public String toString(){
return name+score;
}
@Override
public int compareTo(Student o) {
if(this.score>o.score){
return 1;
}else if(this.score<o.score){
return -1;
}else {
return 0;
}
}
}
Comparator接口
Comparator是比较接口,我们如果需要控制某个类的次序,而该类本身不支持排序(即没有实现Comparable接口),那么我们就可以建立一个“该类的比较器”来进行排序,这个“比较器”只需要实现Comparator接口即可。
有两种情况可以使用实现Comparator接口的方式:
对象类型没有实现Comparable接口,但是又想对两个对象进行比较(大都是这种情况)
对象实现了Comparable接口,但是开发者认为compareTo方法中的比较方式并不是自己想要的那种比较方式
List<Student> stus = new ArrayList<Student>(){
{
add(new Student("张三", 30));
add(new Student("李四", 20));
add(new Student("王五", 60));
}
};
//对users按年龄进行排序
Collections.sort(stus, new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
// 升序
//return s1.getAge()-s2.getAge();
return s1.getAge().compareTo(s2.getAge());
// 降序
// return s2.getAge()-s1.getAge();
// return s2.getAge().compareTo(s1.getAge());
}
});
代码解析:
public class Test {
public static void main(String[] args) {
Collection col1=new ArrayList();
Collection col2=new ArrayList();
col1.add(true);
col1.add("aaa");
col1.add('a');
col1.add(1.2);
System.out.println(col1);
col2.add(111);
col2.add(222);
col2.add(333);
System.out.println(col2);
col1.addAll(col2);
System.out.println(col1);
System.out.println(col2);
System.out.println(col1.contains("aaa"));
System.out.println(col1.containsAll(col2));
System.out.println(col1.remove(111));
System.out.println(col1);
//System.out.println(col1.retainAll(col2));
System.out.println(col1);
Object[] arr=col2.toArray();
System.out.println(arr);
for(Object obj:col1){
System.out.println(obj);
}
Iterator it=col1.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hXRBLNwc-1678365234938)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]
/*
List :
有序的可重复
新增方法 :
新增了一些根据索引进行操作的方法
遍历方式 :
普通for
增强for
Iterator迭代器
ListIterator列表迭代器
*/
public class Class001_List {
public static void main(String[] args) {
List list = new ArrayList();
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.add("aaa");
System.out.println(list);
//void add(int index, E element) 将指定元素插入此列表中的指定位置(可选操作)。
list.add(3,"ddd");
System.out.println(list);
//boolean addAll(int index, Collection<? extends E> c) 将指定集合中的所有元素插入到指定位置的此列表中(可选操作)。
//E get(int index) 返回此列表中指定位置的元素。
System.out.println(list.get(1));
//int indexOf(Object o) 返回此列表中第一次出现的指定元素的索引,如果此列表不包含该元素,则返回-1。
System.out.println(list.indexOf("aaa"));
//int lastIndexOf(Object o) 返回此列表中指定元素最后一次出现的索引,如果此列表不包含该元素,则返回-1。
System.out.println(list.lastIndexOf("aaa"));
//static <E> List<E> of(E... elements) 返回包含任意数量元素的不可修改列表。
List list2 = List.of(1,2,3,4,5);
System.out.println(list2);
//list2.add(6); //java.lang.UnsupportedOperationException
System.out.println(list2);
//E remove(int index) 删除此列表中指定位置的元素(可选操作)。
System.out.println(list.remove("ddd"));
System.out.println(list.remove(2));
System.out.println(list);
//boolean remove(Object o) 从该列表中删除指定元素的第一个匹配项(如果存在)(可选操作)。
//E set(int index, E element) 用指定的元素替换此列表中指定位置的元素(可选操作)。
System.out.println(list.set(1,"bbbb"));
System.out.println(list);
//List<E> subList(int fromIndex, int toIndex) 返回指定的 fromIndex (包含)和 toIndex (不包括)之间的此列表部分的视图。
System.out.println(list.subList(0,2));
//for
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X9USakaD-1678365234938)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]
public class Class001_Container {
public static void main(String[] args) {
//1)创建集合类型的实例
ArrayList list = new ArrayList();
//2)调用方法实现操作
list.add("张三");
System.out.println(list.size());
list.add(123);
System.out.println(list.size());
list.add(false);
System.out.println(list.size());
list.add('a');
System.out.println(list.size());
list.add(null);
System.out.println(list.size());
System.out.println(list);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AWXy7T8k-1678365234938)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]
/*
自定义容器类型 :
要求只能存储字符串数据
*/
public class Class002_MyContainer {
public static void main(String[] args) {
//1.创建集合对象
MyContainer my = new MyContainer();
//2.调用方法操作集合
my.add("aaa");
System.out.println(my.size());
my.add("bbb");
System.out.println(my.size());
my.add("ccc");
System.out.println(my.size());
System.out.println(my.get(0));
System.out.println(my.get(1));
System.out.println(my.get(2));
}
}
//自定义容器类型
class MyContainer{
private String[] elementData; //记录集合存储的数据
private int size; //记录集合中已存储数据个数
public MyContainer() {
}
//添加数据
public void add(String value) {
//1)是否是第一次添加
if(elementData==null || size==0){
//是,创建数组长度为1
elementData = new String[1];
//数据放入数组
elementData[0] = value;
//长度+1
size++;
return;
}
//不是,创建新数组长度为原数组长度+1
//备份原数组地址
String[] temp = elementData;
elementData = new String[size+1];
//原数组数据拷贝到新数组
for(int i=0;i<size;i++){
elementData[i] = temp[i];
}
//新数据放在新数组最后
elementData[size++] = value;
//长度+1
}
//返回集合中数据个数
public int size(){
return this.size;
}
/**
* 根据索引获取数据
* @param index 索引
* @return index索引位置的数据
*/
public String get(int index) {
if(index<0 || index>=size){
throw new IndexOutOfBoundsException(index+"索引越界啦!!!");
}
return elementData[index];
}
//根据索引进行修改数据
//根据索引进行删除数据
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eysNM60p-1678365234938)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]
Collection : 集合层次结构中的根接口。 集合表示一组对象,称为其元素 。 有些集合允许重复元素而其他集合则不允许。 有些是订购的,有些是无序的。
/*
Collection : 集合层次结构中的根接口。 集合表示一组对象,称为其元素 。 有些集合允许重复元素而其他集合则不允许。 有些是订购的,有些是无序的。
*/
public class Class001_Collection {
public static void main(String[] args) {
//创建集合对象
Collection col1 = new ArrayList();
Collection col2 = new ArrayList();
//boolean add(E e) 确保此集合包含指定的元素(可选操作)。
col1.add("你好");
col1.add(false);
col1.add(null);
col1.add(1.23);
col1.add(100);
//boolean addAll(Collection<? extends E> c) 将指定集合中的所有元素添加到此集合中(可选操作)。
col2.add("aaa");
col2.add("bbb");
col2.add("ccc");
System.out.println(col1);
System.out.println(col2);
col1.addAll(col2);
System.out.println(col1);
System.out.println(col2);
//void clear() 从此集合中删除所有元素(可选操作)。
//boolean isEmpty() 如果此集合不包含任何元素,则返回 true 。
//System.out.println(col1.isEmpty());
//col1.clear();
//System.out.println(col1.isEmpty());
//boolean contains(Object o) 如果此collection包含指定的元素,则返回 true 。
System.out.println(col1.contains(100));
//boolean containsAll(Collection<?> c) 如果此集合包含指定集合中的所有元素,则返回 true 。
System.out.println(col1.containsAll(col2));
//boolean remove(Object o) 从此集合中移除指定元素的单个实例(如果存在)(可选操作)。
System.out.println(col1.remove("aaa"));
System.out.println(col1);
System.out.println(col2);
//boolean removeAll(Collection<?> c) 删除此集合的所有元素,这些元素也包含在指定的集合中(可选操作)。
//col1.removeAll(col2);
//System.out.println(col1);
//boolean retainAll(Collection<?> c) 仅保留此集合中包含在指定集合中的元素(可选操作)。
//System.out.println(col1.retainAll(col2));
System.out.println(col1);
//Object[] toArray() 返回包含此集合中所有元素的数组。
System.out.println(Arrays.toString(col1.toArray()));
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6faczOrC-1678365234939)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]
/*
遍历方式 :
1.foreach
2.iterator迭代器
*/
public class Class002_Each {
public static void main(String[] args) {
Collection col = new ArrayList();
col.add("aaa");
col.add("bbb");
col.add("ccc");
col.add("ddd");
col.add("eee");
//foreach
for(Object obj:col){
System.out.println(obj);
}
//iterator
//1)获取迭代器实例
Iterator it = col.iterator();
//2)判断是否存在下一个元素
while(it.hasNext()){
//3)获取下一个元素
Object obj = it.next();
System.out.println(obj);
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fqI9YpNi-1678365234939)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q9jbL6Dt-1678365234939)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]
public class Class001_List {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("bbb");
list.add("ddd");
list.add("eee");
list.add("fff");
list.add("aaa");
System.out.println(list);
//void add(int index, E element) 将指定元素插入此列表中的指定位置(可选操作)。
list.add(0,"aaa");
System.out.println(list);
//E set(int index, E element) 用指定的元素替换此列表中指定位置的元素(可选操作)。
System.out.println(list.set(1,"ccc"));
System.out.println(list);
//E get(int index) 返回此列表中指定位置的元素。
System.out.println(list.get(0));
System.out.println(list.get(1));
//int indexOf(Object o) 返回此列表中第一次出现的指定元素的索引,如果此列表不包含该元素,则返回-1。
//int lastIndexOf(Object o) 返回此列表中指定元素最后一次出现的索引,如果此列表不包含该元素,则返回-1。
System.out.println(list.indexOf("aaa"));
System.out.println(list.lastIndexOf("aaa"));
//static <E> List<E> of(E e1, E e2) 返回包含两个元素的不可修改列表。
List<Integer> ls = List.of(1,2,3,4,5);
System.out.println(ls);
//ls.remove(1); //java.lang.UnsupportedOperationException
//E remove(int index) 删除此列表中指定位置的元素(可选操作)。
//boolean remove(Object o) 从该列表中删除指定元素的第一个匹配项(如果存在)(可选操作)。
list.remove("aaa");
list.remove(1);
System.out.println(list);
//定义一个List集合,存储整型数据,在做remove(1),默认根据内容移出还是索引移出
//List<E> subList(int fromIndex, int toIndex) 返回指定的 fromIndex (包含)和 toIndex (不包括)之间的此列表部分的视图。
List ls2 = list.subList(1,3);
System.out.println(ls2);
//for
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7sjlnvOG-1678365234939)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]
课堂代码小练习
/*
定义List集合,存储你喜欢的漫威英雄人物,如果存在灭霸,就添加一个惊奇队长(可以使用多种方式进行判断,包括所有的遍历方式)
*/
public class Class002_Practice {
public static void main(String[] args) {
List list = new ArrayList();
list.add("钢铁侠");
list.add("美国队长");
list.add("黑寡妇");
list.add("灭霸");
list.add("猩红女巫");
System.out.println(list);
//1.contains
if(list.contains("灭霸")){
list.add("惊奇队长");
}
//2.for
for(int i=0;i<list.size();i++){
if("灭霸".equals(list.get(i))){
list.add("惊奇队长");
}
}
//3.foreach
for(Object obj:list){ //java.util.ConcurrentModificationException
if("灭霸".equals(obj)){
list.add("惊奇队长");
}
}
//4.iterator
Iterator it1 = list.iterator(); //java.util.ConcurrentModificationException
while(it1.hasNext()){
if("灭霸".equals(it1.next())){
list.add("惊奇队长");
}
}*/
//ListIterator<E> listIterator() 返回此列表中元素的列表迭代器(按适当顺序)。
//ListIterator<E> listIterator(int index) 从列表中的指定位置开始,返回列表中元素的列表迭代器(按正确顺序)。
//1)获取迭代器对象
ListIterator it2 = list.listIterator();
//2)判断是否存在下一个元素|是否存在上一个元素
while (it2.hasNext()){
//3)获取下一个元素|获取上一个元素
//System.out.println(it2.nextIndex()+"------->"+it2.next());
if("灭霸".equals(it2.next())){
it2.add("惊奇队长");
}
}
while (it2.hasPrevious()) {
System.out.println(it2.previousIndex()+"---->"+it2.previous());
}
System.out.println(list);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MlayjJHt-1678365234939)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]
/*
定义List集合存储你喜欢的漫威英雄人物,如果存在灭霸,就添加一个惊奇队长
判断方式 : 要求使用contains与所有遍历方式
*/
public class Class002_Each {
public static void main(String[] args) {
List<String> heroes = new ArrayList<>();
heroes.add("钢铁侠");
heroes.add("美国队长");
heroes.add("蜘蛛侠");
heroes.add("灭霸");
System.out.println("原始的英雄列表:" + heroes);
if (heroes.contains("灭霸")) {
heroes.add("惊奇队长");
System.out.println("英雄列表中包含灭霸,添加惊奇队长后的列表:" + heroes);
}
// 使用for循环遍历
System.out.print("使用for循环遍历英雄列表:");
for (int i = 0; i < heroes.size(); i++) {
System.out.print(heroes.get(i) + " ");
}
System.out.println();
// 使用增强for循环遍历
System.out.print("使用增强for循环遍历英雄列表:");
for (String hero : heroes) {
System.out.print(hero + " ");
}
System.out.println();
// 使用迭代器遍历
System.out.print("使用迭代器遍历英雄列表:");
Iterator<String> iterator = heroes.iterator();
while (iterator.hasNext()) {
System.out.print(iterator.next() + " ");
}
System.out.println();
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7CDCuHJg-1678365234940)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]
.listIterator();
//2)判断是否存在下一个元素|是否存在上一个元素
while (it2.hasNext()){
//3)获取下一个元素|获取上一个元素
//System.out.println(it2.nextIndex()+“------->”+it2.next());
if(“灭霸”.equals(it2.next())){
it2.add(“惊奇队长”);
}
}
while (it2.hasPrevious()) {
System.out.println(it2.previousIndex()+"---->"+it2.previous());
}
System.out.println(list);
}
}
[外链图片转存中...(img-MlayjJHt-1678365234939)]
```java
/*
定义List集合存储你喜欢的漫威英雄人物,如果存在灭霸,就添加一个惊奇队长
判断方式 : 要求使用contains与所有遍历方式
*/
public class Class002_Each {
public static void main(String[] args) {
List<String> heroes = new ArrayList<>();
heroes.add("钢铁侠");
heroes.add("美国队长");
heroes.add("蜘蛛侠");
heroes.add("灭霸");
System.out.println("原始的英雄列表:" + heroes);
if (heroes.contains("灭霸")) {
heroes.add("惊奇队长");
System.out.println("英雄列表中包含灭霸,添加惊奇队长后的列表:" + heroes);
}
// 使用for循环遍历
System.out.print("使用for循环遍历英雄列表:");
for (int i = 0; i < heroes.size(); i++) {
System.out.print(heroes.get(i) + " ");
}
System.out.println();
// 使用增强for循环遍历
System.out.print("使用增强for循环遍历英雄列表:");
for (String hero : heroes) {
System.out.print(hero + " ");
}
System.out.println();
// 使用迭代器遍历
System.out.print("使用迭代器遍历英雄列表:");
Iterator<String> iterator = heroes.iterator();
while (iterator.hasNext()) {
System.out.print(iterator.next() + " ");
}
System.out.println();
}
}
[外链图片转存中…(img-7CDCuHJg-1678365234940)]