学习目标
- 核心掌握List集合
- 了解Set集合
1.List<T>
● java.util.List。有序列表。
● List集合元素的特点:有序表示存取有序(因为有索引)而且可以重复
● List常用实现类: ArrayList、LinkedList、Vector等
1.1 常用方法
1.2 使用方法
- 测试List集合的使用方法
private static void demo1() {
List<Integer> numList = new ArrayList<>();
//1.新增
numList.add(100);
numList.add(20);
numList.add(10);
numList.add(2);
//2.删除
numList.remove(Integer.valueOf(20));//删除元素:20
numList.removeIf(new Predicate<Integer>() {
@Override
public boolean test(Integer num) {
return Integer.valueOf(20).equals(num);
}
});
numList.removeIf(Integer.valueOf(20)::equals);
//numList.clear();
//3.修改
Integer num = numList.set(0, 200);
System.out.println("num:"+num);
//4.查询
Integer num = numList.get(0);
System.out.println(num);
//5.其它方法
System.out.println(numList.size());
System.out.println(numList.contains(100));
System.out.println(numList.indexOf(20));//获得指定数据第一次出现的索引位置 -1
System.out.println(numList.lastIndexOf(20));
System.out.println(numList.isEmpty());
numList.sort();//对集合数据进行排序 default void sort(Comparator<? super E> c);
numList.sort(Comparator.naturalOrder());//对于Integer而言 其实底层已经提供了排序规则
//List集合元素类型已经实现过Comparable接口的话,使用List.sort()建议这样写
numList.sort(Comparator.reverseOrder());
System.out.println(numList);
numList.replaceAll(new UnaryOperator<Integer>() {//修改满足条件的多个数据
@Override
public Integer apply(Integer integer) {
if (integer != null) {
if (integer >= 100) {
integer = 1;
}
}
return integer;
}
});
/* numList.replaceAll(integer->{
if (integer != null) {
if (integer >= 100) {
integer = 1;
}
}
return integer;
});*/
}
- 了解subList方法
private static void demo3() {
List<String> list = new ArrayList<>();
Collections.addAll(list, "a", "b", "c", "abc", "ccc");
//List<T> subList(startIndex,endIndex);
List<String> subList = list.subList(0, 3);
//subList之后,对截取之后的集合对象执行更新操作,都会还原到原对象。
//截取的集合对象: 记着原集合对象的 root
//subList.add("hello");
//subList.remove(0);
//subList.set(0,"aaaa");
//截取之后,操作原集合(新增/遍历截取集合/删除/修改)。会出现ConcurrentModificationException。
list.remove(0);// 已经删除。modCount 触发的fail-fast
System.out.println("subList:" + subList);
System.out.println("list:" + list);
//Arrays.asList();//数组转集合
List<String> stringList = Arrays.asList("a", "b", "c", "abc", "ccc");
stringList.add("hello");
System.out.println(stringList);
}
1.3 遍历集合
private static void demo2() {
//方便创建对象并添加多个集合数据 还要满足后期更新。操作集合的工具类 java.util.Collections
List<String> list = new ArrayList<>();
Collections.addAll(list, "a", "b", "c", "abc", "ccc");
//遍历List集合-----> 任意一种方式都可以
//1.增强for
for (String s : list) {
System.out.println(s);
}
//2.迭代器/listIterator
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String s = it.next();
System.out.println(s);
}
/*ListIterator<String> listIterator = list.listIterator();
while (listIterator.hasNext()) {
String s = listIterator.next();
System.out.println(s);
}
System.out.println("-----------------");
//光标默认在最左端
while (listIterator.hasPrevious()) {
String s = listIterator.previous();
System.out.println("s:"+s);
}*/
//3.普通for
int size = list.size();
for (int index = 0; index < size; index++) {
System.out.println(list.get(index));
}
//4.forEach
list.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
list.forEach(System.out::println);
System.out.println(list);
}
1.4 常用实现类
● 有序的特点: (有2种解释)
● 1.有索引位置(新增的元素顺序与遍历时候的顺序一致的)
● 2.可以按照排序规则排序(升序/降序)
● 常见数据结构:
● 1. 数组
● 2. 链表:
1.单向链表: 当前数据/元素/节点 下一个节点的引用。
2.双向链表: 上一个节点的引用 当前数据 下一个节点的引用。
● 3. 红黑树
● 4. hash表
实现类 | 底层数据结构 | 性能/效率 | 线程安全 |
---|---|---|---|
ArrayList | 动态数组 | 查询/修改效率高。新增/删除效率低 | 不安全 |
LinkedList | 双向链表 | 查询/修改效率低。新增/删除效率高。 | 不安全 |
Vector | 动态数组 | 单线程里面, 功能等价于ArrayList。并发,所有的功能性能偏低 | 安全 |
● ArrayList底层是数组。 因为数组内存空间连续所以查询性能较高(时间复杂度 O(1) ),删除元素和指定位置新增涉及到数组元素的移动 所以性能较低
● LinkedList底层是双向链表 内存空间不连续;链表的查询性能较低(要么从头查 要么从尾部查询 O(n)),删除和新增性能较高(只需要改动前后2个节点)
1.4.1 ArrayList<T>
● 创建集合对象的时候 使用泛型进行约束
● 底层是数组 ,当调用无参构造的时候,内部数组第一次扩容长度为10;当调用有参构造的时候 直接new一个指定长度的数组(建议使用有参构造 给一个大概的容量值 避免内部多次扩容降低性能)
● 底层是数组 因为数组内存空间连续所以查询性能较高,删除元素和指定位置新增涉及到数组元素的移动 所以性能较低
1.层级
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable{}
//RandomAccess: 快速随机访问。 空接口,标记接口。使用index遍历集合数据比迭代器效率快很多的。
2.常用构造
1. ArrayList() ;// 创建ArrayList集合对象 并初始化容量为10.---->数组的length。
2. ArrayList(int initialCapacity);//指定初始化容量数据 推荐。10
//initialCapacity: 存储的最大元素个数/负载因子+1;
3. ArrayList(Collection<? extends E> c);//将一个集合的数据转换成ArrayList
3.基本使用
/**
- 泛型 元素类型 加约束
*/
public static void arrayListMethod() {
ArrayList arrayList = new ArrayList();
arrayList.add(“abc”);
arrayList.add(“123”);
// 加了泛型之后 取出来的元素 直接就是String类型了
String str = arrayList.get(1);
// 集合中都是对象 没有基本数据类型
ArrayList arrayList2 = new ArrayList<>();
arrayList2.add(123);
}
1.4.2 LinkedList<T>
● 底层是链表 双向链表 内存空间不连续
● 链表的查询性能较低,删除和新增性能较高(新增与删除最多只需要改动前后2个节点)
1.层级
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, Serializable{}
// 双链表实现了List和Deque接口。 实现所有可选列表操作,并允许所有元素(包括null )
2.常用构造
1.LinkedList();//推荐使用
2.LinkedList(Collection<? extends E> c)
3.基本使用
private static void demo1() {
LinkedList<String> linkedList = new LinkedList<>();
//1.新增
/*linkedList.add("a");
linkedList.add(1,"b");
linkedList.addFirst("hello");
linkedList.addLast("abc");
linkedList.push("111");*///addFirst add(0,"111");
linkedList.add("a");
linkedList.add("b");
linkedList.add("c");
linkedList.add("d");
linkedList.offer("1");//add();
linkedList.offerFirst("0");
linkedList.offerLast("100");
//2.删除
linkedList.remove("a");
linkedList.remove(0);
//删除第一个数据
linkedList.removeFirst();
//删除最后一个数据
linkedList.remove(linkedList.size() - 1);
linkedList.removeLast();
linkedList.pollLast();
//3.修改
linkedList.set(0,"abc");
//4.查询
system.out.println(linkedList.get(0));
System.out.println(linkedList.getFirst());
System.out.println(linkedList.element());
System.out.println(linkedList.peek());
System.out.println(linkedList.peekFirst());
/*System.out.println(linkedList.get(linkedList.size() - 1));
System.out.println(linkedList.getLast());
System.out.println(linkedList.peekLast());*/
System.out.println(linkedList);
}
1.4.3 Vector<T>
● 底层也是数组,是一个线程安全的集合类.
● 线程安全的ArrayList的变体。
● 根本原因: Vector中所有的方法都使用synchronized。
● 了解此类即可。
1.层级
public class Vector<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, Serializable{}
2.常用构造
1. Vector();//初始化容量为10
2. Vector(int initialCapacity) ;//建议使用
3.线程安全
public class VectorDemo {
//private static Vector<Integer> list = new Vector<>(100);
private static List<Integer> list = Collections.synchronizedList(new ArrayList<>(100));
//需要将线程不安全的集合对象 转换成线程安全的集合对象 Collections.syn
public static void main(String[] args) {
demo2();
}
private static void demo2() {
//开启多个任务 多个线程 同时操作list对象 新增数据
//模拟: 创建10个线程 每个线程执行100次 add 1-100 1000个数据
List<Thread> threadList = new ArrayList<>(10);
for (int i = 0; i < 10; i++) {
threadList.add(new Thread(VectorDemo::forDemo));
}
threadList.forEach(Thread::start);//启动10个线程
//必须等待前10个线程的死亡
try {
for (Thread thread : threadList) {
thread.join();
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("size:" + list.size());//1000
}
private static void forDemo() {
for (int num = 1; num <= 100; num++) {
list.add(num);
}
}
}
2.Set<T>
● 实现了Set接口的实现类的集合元素数据: 无序 且不可重复
● 实现类有:HashSet ,LinkedHashSet, TreeSet等
● Set集合的每个数据都是作为Map的key来维护的。
2.1 常用方法
在Set集合中,没有独有的方法。所有的方法都是继承的Collection父接口。
2.2 使用方法
private static void demo1() {
Set<Integer> set = new HashSet<>();//唯一 无序
set.add(10);
set.add(20);
set.add(null);
//删除
//set.remove(10);
//.....
System.out.println(set);
System.out.println(set.size());
}
2.3 遍历集合
/**
* Set 遍历方式有3种 因为没有索引 所以不能使用普通for循环
*/
public static void demo2() {
HashSet<String> hashSet = new HashSet<>();
hashSet.add("aa");
hashSet.add("bb");
hashSet.add("cc");
System.out.println("-----1---------");
for (String str : hashSet) {
System.out.println(str);
}
System.out.println("------2---------");
Iterator<String> iterator = hashSet.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
System.out.println("--------3----------");
hashSet.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
hashSet.forEach((a) -> {
System.out.println(a);
});
hashSet.forEach(System.out::println);
}
2.4 常用实现类
数据结构 | 元素是否可以为null | 线程安全 | |
---|---|---|---|
HashSet | hash表+位桶(数组)+单向链表+红黑树。(HashMap维护HashSet的数据) | 可以 | 不安全 |
LinkedHashSet | 双向链表+hash表。(LinkedHashMap) | 可以 | 不安全 |
TreeSet | 红黑树(TreeMap) 有序(自然顺序排列)平衡 | 不可以 | 不安全 |
2.4.1 HashSet<T>
1.常用构造
1.HashSet() //HashMap实例具有默认初始容量(16)和负载因子(0.75)。
2.HashSet(int initialCapacity) // initialCapacity = 存储的最大的元素个数/负载因子+1
2.基本使用
private static void demo1() {
HashSet<Integer> hashSet = new HashSet<>();
//hashSet.add(1);
Collections.addAll(hashSet, 1, 10, 8, 100, 2, 80);
hashSet.add(100);
hashSet.add(null);
//元素是完全没有顺序 新增顺序与遍历顺序不一样
//元素没有索引位置
System.out.println(hashSet);
}
2.4.2 LinkedHashSet<T>
- LinkedHashSet是HashSet的子类。
private static void demo2() {
LinkedHashSet<Integer> linkedHashSet = new LinkedHashSet<>();
Collections.addAll(linkedHashSet, 1, 10, 8, 100, 2, 80,null,80);
System.out.println(linkedHashSet);
//新增的顺序与遍历的顺序一致的
}
2.4.3 TreeSet<T>
1.常用构造
元素是有序(顺序)----> 按照自然顺序排列(排序)---->升序/降序
要求TreeSet集合的元素类型必须提供“排序规则”。
元素不能存储null(不能调用方法hashcode)
1. TreeSet() //根据其元素的自然排序进行排序. 要求集合元素类型必须实现java.lang.Comparable
2. TreeSet(Comparator<? super E> comparator) //自定义外部比较器对象 对集合元素进行排序
2.基本使用
private static void demo3() {
TreeSet<Integer> treeSet = new TreeSet<>();//会集合元素排序---->集合元素类型的排序规则---> Integer
//前提是无参构造 默认按照升序进行排列
//treeSet.add(null);
treeSet.add(10);
treeSet.add(1);
treeSet.add(0);
treeSet.add(15);
treeSet.add(12);
System.out.println(treeSet);
System.out.println("min:" + treeSet.first());
System.out.println("max:" + treeSet.last());
}
3.集合存储对象
● 需求: 使用集合存储多个用户信息。
3.1 List<T>
@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class UserInfo {
private Integer id;
private String name;
private Integer age;
}
private static void demo1() {
//创建多个用户对象 存储到集合中
//ArrayList
//List集合可以存储多个重复的数据
List<UserInfo> userInfoList = new ArrayList<>(10);
userInfoList.add(new UserInfo(1, "张三", 20));
userInfoList.add(new UserInfo(1, "张三", 20));
userInfoList.add(new UserInfo(1, "张三", 20));
userInfoList.add(new UserInfo(1, "张三", 20));
userInfoList.forEach(System.out::println);
}
3.2 HashSet<T>
@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class UserInfo {
private Integer id;
private String name;
private Integer age;
//重写hashcode+equals 定义2分对象相等的规则
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
UserInfo userInfo = (UserInfo) o;
return
Objects.equals(name, userInfo.name)
;
}
@Override
public int hashCode() {
return Objects.hash( name);
}
//......
}
private static void demo2() {
//使用HashSet存储多个用户对象
//无序且唯一的
//使用Set存储自定义类型对象的时候 一定自己重写equals+hashcode 底层数据结构是重建hash
//根据重写的equals+hashcode 判断对象是否是相同的对象
Set<UserInfo> userInfoSet = new HashSet<>();
userInfoSet.add(new UserInfo(1, "张三", 20));
userInfoSet.add(new UserInfo(2, "张三", 20));
userInfoSet.add(new UserInfo(3, "张三", 20));
userInfoSet.add(new UserInfo(4, "张三", 20));
userInfoSet.forEach(System.out::println);
}
3.3 TreeSet<T>
● TreeSet底层的数据结构是红黑树。
● 由于TreeSet集合的元素要按照排序规则进行排序。所以使用TreeSet集合存储自定义类对象,一定要提供排序规则。
● 可以根据不同的TreeSet构造创建对象:
● TreeSet(); 无参构造创建对象。要求集合元素类型必须实现Comparable接口。
● TreeSet(Comparator comparator); 有参构造创建对象。集合元素类型不必实现Comparable,这个时候会使用外部比较器Comparator里面定义的排序规则对集合元素排序。
- 写法1:
@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class UserInfo implements Comparable<UserInfo> {
private Integer id;
private String name;
private Integer age;
//自定义排序规则
//不需要重写equals+hashcode TreeSet底层不是hash维护的,与这2个方法没有一点关系,只看排序规则。
//在排序规则里面,相关属性相同的对象就会认为是相同的对象。
@Override
public int compareTo(UserInfo userInfo) {
return userInfo.age.compareTo(this.age);
}
}
private static void demo3() {
//使用TreeSet存储多个用户对象
//元素会排序----->排序规则是什么?
//使用TreeSet无参构造创建对象 要求集合元素类型必须实现java.lang.Comparable
//TreeSet与hash没有关系 底层是树结构维护的 只看排序规则里面的属性的数据
TreeSet<UserInfo> userInfoTreeSet = new TreeSet<>();
userInfoTreeSet.add(new UserInfo(1, "张三", 20));
userInfoTreeSet.add(new UserInfo(2, "张三", 20));
userInfoTreeSet.add(new UserInfo(3, "张三", 12));
userInfoTreeSet.add(new UserInfo(4, "张三", 25));
userInfoTreeSet.forEach(System.out::println);
}
- 写法2:
@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class UserInfo {
private Integer id;
private String name;
private Integer age;
}
private static void demo4() {
// TreeSet<UserInfo> userInfoTreeSet = new TreeSet<>(new Comparator<UserInfo>(){
// @Override
// public int compare(UserInfo user1, UserInfo user2) {
// return user1.getAge().compareTo(user2.getAge());
// }
// });
// TreeSet<UserInfo> userInfoTreeSet = new TreeSet<>((user1,user2)->user1.getAge().compareTo(user2.getAge()));
TreeSet<UserInfo> userInfoTreeSet = new TreeSet<>(Comparator.comparing(UserInfo::getAge));
userInfoTreeSet.add(new UserInfo(1, "张三", 20));
userInfoTreeSet.add(new UserInfo(2, "张三", 20));
userInfoTreeSet.add(new UserInfo(3, "张三", 12));
userInfoTreeSet.add(new UserInfo(4, "张三", 25));
userInfoTreeSet.forEach(System.out::println);
}
}