集合...

集合

数组 与 集合(容器)的区别 :
共同点 :
存储多个数据
不同点 :
数组 :
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的操作)判断要删除的节点是否是最后一个节点
如果要删除的节点是最后一个节点(next
null)
将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)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值