单列集合List和单列集合Collections工具类

单列集合List

重点

在这里插入图片描述

List集合

概述:List接口就是继承了Collection接口的子接口,拓展了一些新的方法。

特点:List体系中的实现类都具有可重复性,有序,有索引。

List集合操作索引的特有方法

在这里插入图片描述

List<String> nameList = new List<>();
nameList.add("小明");
nameList.add("小军");
nameList.add("小李");

(1)public void add(int index, E e):添加元素到指定的索引(原索引后移,注意越界问题索引)。

nameList.add(3, "小花");
System.out.println("nameList : " + nameList);

(2)public E remove(int index) : 删除索引的元素 将被删除的元素返回。

String removeElement = nameList.remove(0);
System.out.println("nameList : " + nameList);
System.out.println("removeElement : " + removeElement);

(3)public E set(int index,E e):将指定索引的元素替换为参数e并且将被替换的元素返回。

String replaceElement = nameList.set(2, "小王");
System.out.println("nameList : " + nameList);
System.out.println("replaceElement : " + replaceElement);

(4)public E get(int index):获取指定索引的元素。

String element = nameList.get(0);
System.out.println("0索引的元素是 : " + element);

List集合的四种遍历方式

迭代器遍历:

Iterator<String> it = nameList.iterator();
while (it.hasNext()) {
    String element = it.next();
    System.out.println("迭代器 : " + element);
}

增强for循环:

for (String element : nameList) {
    System.out.println("增强For : " + element);
}

Lambda表达式(函数式):

nameList.forEach(s -> System.out.println("Lambda : " + s));

普通for循环:

for (int i = 0; i < nameList.size(); i++) {
    String element = nameList.get(i);
    System.out.println("普通For : " + element);
}

ArrayList集合

ArrayList底层数据结构

ArrayList底层是基于数组结构来保存数据的。ArrayList适合数据量小有频繁查询的场景,不适合数据量大又频繁增删的场景。

在这里插入图片描述

数组结构在创建的时候长度就已经确定了(开辟一块连续的空间),数组的内存空间是连续的。

特点:

  1. 查询快:基于索引查询,查询任意索引的时间相同。
  2. 增删慢:因为要保证连续的特点,删除元素需要后面的元素前移,添加元素需要原始的元素后移,可能还需要扩容。

ArrayList底层基于一个elementData的数组来保存实际的元素。

(1)当基于无参构造创建ArrayList的时候,底层的elementData默认是一个长度为0的数组。

(2)第一次添加元素的时候,会将elementData初始化为长度为10的数组。

(3)当数组内容满了需要扩容,则每次扩容1.5倍。

(4)如果可以明确之后存储元素的大概个数,推荐使用哪个ArrayList的有参构造!

LinkedList集合

LinkedList底层数据结构

LinkedList底层的接口是双向链表。

单向链表:

在这里插入图片描述

特点:

增删快:因为链表添加节点不会影响整体。

查询慢:由于内存不连续,导致查询任何一个内容,都要从头结点开始查询。

双向链表:

在这里插入图片描述

特点:

增删快:因为链表添加节点不会影响整体。

查询慢:由于内存不连续,导致查询任何一个内容,可以基于离头近还是离尾近找到对应节点开始查询。

LinkedList特有操作首尾的方法

在这里插入图片描述

代码示例:

//public void addFirst(E e) : 添加元素作为链表头结点 => add(0,元素);
nameLinkedList.addFirst("小张");
//public void addLast(E e) : 添加元素作为链表尾结点 => add
nameLinkedList.addLast("小王");
System.out.println("nameLinkedList : " + nameLinkedList);

//public E removeFirst() : 删除头结点(返回被删除的元素) 在模拟一些逻辑结构的时候有移除并返回的需求 remove(0)
nameLinkedList.removeFirst();
//public E removeLast() : 删除尾结点(返回被删除的元素) remove(长度-1)
nameLinkedList.removeLast();
System.out.println("nameLinkedList : " + nameLinkedList);

//public E getFirst() : 获取头节点元素 get(0)
System.out.println("First Node : " + nameLinkedList.getFirst());
//public E getLast() : 获取尾节点元素 get(长度-1)
System.out.println("Last Node : " + nameLinkedList.getLast());

栈结构与队列结构

队列结构

队列也是一种数据结构可以用来存储数据。特点:先进先出FIFO。

图解:

在这里插入图片描述

代码示例:

//泛型 T为占位符,对象调用时明确数据类型
public class MyQueue<T> {
    //基于LinkedList保存实际存储在队列中的数据
    private LinkedList<T> dataList = new LinkedList<>();
    //存储数据
    public void save(T t) {
        dataList.addLast(t);
    }
    //获取数据
    public T get() {
        return dataList.removeFirst();
    }
}

栈结构

栈也是一种数据结构可以用来存储数据。特点:先进后出FILO。

图解:

在这里插入图片描述

代码示例:

public class MyStack<T> {
    //基于LinkedList保存实际存储在栈中的数据
    private LinkedList<T> dataList = new LinkedList<>();

    //存储数据到栈中的方法(压栈push)
    public void push(T e) {
        dataList.addLast(e);
    }

    //获取栈中数据的方法(弹栈pop)
    public T pop() {
        return dataList.removeLast();
    }
}

Set集合

Set接口以及实现类通用特点:

不保存重复元素,没有索引。

Set集合体系:

在这里插入图片描述

HashSet集合

HashSet是使用频率最高的Set集合的实现类。

特点:

(1)无序:元素存入的顺序与取出的顺序不保证一致。

(2)保证元素唯一:当add方法添加元素存在时不保存。

(3)没有索引

HashSet保证元素唯一的方式

HashSet底层的数据结构是哈希表,是一种复合型的数据结构,由数组+链表组成的哈希表。

特点:添加快,删除快,查询快,保证元素唯一!

哈希值:

哈希值是一串int数字,任何对象都可以基于哈希函数计算出自己的哈希值,重复率极低。

哈希值的功能唯一,就是为了服务哈希表的进行去重的。

HashSet使用时注意事项

容易出的错误:

自定义的类对象在HashSet集合中使用时不重写hashCode方法,导致相同的数据可以存进去数值。

(1)未重写hashCode方法

当自定义的类没有重写hashCode方法,则对象调用hashCode方法是基于地址值生成的。

//(1)创建3个Person对象(有参构造)
Person p1 = new Person("小刘", 23);
Person p2 = new Person("小刘", 23);
Person p3 = new Person("小李", 24);

//(2)直接基于Person对象调用hashCode方法即可获取该对象的哈希值(未重写:基于地址)
System.out.println("p1的哈希值是:" + p1.hashCode()); //990368553
System.out.println("p2的哈希值是:" + p2.hashCode()); //1828972342
System.out.println("p3的哈希值是:" + p3.hashCode()); //1452126962

哈希表无法保证内容相同的元素唯一!和

(2)重写hashCode方法

当自定义的类重写了hashCode方法,对象调用是根据内容生成的哈希值。

//(1)创建3个Person对象(有参构造)
Person p1 = new Person("小刘", 23);
Person p2 = new Person("小刘", 23);
Person p3 = new Person("小李", 24);

//(2)直接基于Person对象调用hashCode方法即可获取该对象的哈希值(重写:基于内容)
System.out.println("p1的哈希值是:" + p1.hashCode()); //745705612
System.out.println("p2的哈希值是:" + p2.hashCode()); //745705612
System.out.println("p3的哈希值是:" + p3.hashCode()); //663508400

总结:

(1)如果想要HashSet保存自定义的元素并且按照内容进行出重,保证唯一,自定义的类必须重写hashCode方法和equals方法。

(2)HastSet并不是真的无序,只是按照元素的哈希值在哈希表中散列之后的顺序保存的。

LinkedHashSet集合

特点:

(1)有序:可以保证元素的存入顺序与取出的顺序一致。

(2)保证元素的唯一。

(3)没有索引。

LinkedHashSet保证元素唯一的方式

在这里插入图片描述

想要维护元素的存取有序势必要使用更多的内存空间,取舍的问题根据实际情况选择。

TreeSet集合

TreeSet的底层基于红黑树。

特点:

(1)元素会按照指定的规则进行排序。

(2)保证元素的唯一【基于排序的规则】。

(3)没有索引。

TreeSet的构造方法

public TreeSet(Compartor c):基于指定排序规则创建TreeSet集合。

代码示例:

//(1)创建TreeSet集合并且指定排序规则(TreeSet保存Person,Person的排序规则)
TreeSet<Person> personTreeSet = new TreeSet<>(new Comparator<Person>() {
    @Override
    public int compare(Person o1, Person o2) {
    	return o2.getAge() - o1.getAge();
    }
});
//注意:当添加元素搭配TreeSet自动调用排序规则方法为元素进行排序
personTreeSet.add(p1);
personTreeSet.add(p3);
personTreeSet.add(p4);
personTreeSet.add(p5);

TreeSet集合保证元素唯一的方式

TreeSet底层是红黑树,所以保证元素唯一的方式和元素的哈希值没有一点关系。基于排序规则答返回值保证唯一,如果添加的元素和已存在的元素经过对比返回值为0,就不进行添加。

TreeSet集合的极端问题解决方法

当给出的排序规则过于简单,有可能会造成某些认为不相同的元素也会被排除掉。

例如:

Person p1 = new Person("小刘",21);
Person p2 = new Person("小李",21);
//排序规则
TreeSet<Person> personTreeSet = new TreeSet<>(new Comparator<Person>() {
    @Override
    public int compare(Person o1, Person o2) {
        //主要条件
        return result = o2.getAge() - o1.getAge();
    }
});

只简单的根据年龄进行判断,年龄虽然相同但是不是一个对象,明显不是一个相同的,TreeSet会认为是相同的。

改进:

TreeSet<Person> personTreeSet = new TreeSet<>(new Comparator<Person>() {
    @Override
    public int compare(Person o1, Person o2) {
        //主要条件
        int result = o2.getAge() - o1.getAge();
        //如果主要条件是0,只能说明o1和o2的age是相同的,但是并不能说明其他项也是相同。
        if (result == 0)
            result = o1.equals(o2) ? 0 : 1;
        return result;
    }
});

可变参数

可变参数就是参数的个数可以发生改变。

可变参数的格式

可变参数一般作为方法的参数列表。

权限修饰符 返回值类型 方法名(数据类型... 变量名){}
public static void show(int...a){}

可变参数的调用

public static void show(int... a) {
    //可变参数的本质就是一个数组(和之前的区别在于,由Java默认将多个参数收集到数据中)
    for (int i : a) {
        System.out.println(i);
    }
}

//(1)调用可变参数的方法-不传递数据 (√)
show(); //数组长度为0

//(2)调用可变参数的方法-可以传递任意个数的数据,要是用,分隔 (√)
show(10, 20, 30, 40, 50, 60, 70, 80, 90); //数组中收集多少个数据

//(3)调用可变参数的方法-可以传递null值(没什么意义)
show(null); //int[] a = null; 可能会空指针

//(4)调用可变参数的方法-可以传递一个对应的数组(没什么意义)
int[] arr = new int[]{2, 3, 4, 5};
show(arr); //int[] a = arr; 地址值

可变参数的本质

java中会自动将多个参数收集到一起封装到一个数组中传递给可变参数,可变参数的本质上就是一个数组。

在方法中可以使用数组的方法。

可变参数的注意事项

(1)一个方法的可变参数只能有一个。

(2)一个方法如果有普通参数与可变参数,那么可变参数必须放到形参列表的最后面。

单列集合的工具类Collections

Collections常用方法

在这里插入图片描述

Collections的两种排序规则

自定义排序

调用Collections的排序方法,传入需要排序的单列集合和自定义排序规则。

代码示例

Collections.sort(dogList, new Comparator<Dog>() {
    @Override
    public int compare(Dog o1, Dog o2) {
        return o2.getAge() - o1.getAge();
    }
});

类自身包含排序规则

需要让排序的类实现Comparable接口,实现compareTo方法,声明好排序规则。

private static class Dog implements Comparable<Dog> {
	//this和o进行对比 this:o1 o:o2
    @Override
    public int compareTo(Dog o) {
        return this.age - o.age;
    }
}

在排序的时候就可以只传递集合。

Collections.sort(dogList);

自定义给出的排序规则优先级高于自身包含的排序规则。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值