集合
是Java中提供的一种容器,可以用来存储多个数据
集合和数组都是容器,区别是:
- 数组的长度是固定的 — 集合的长度是可变的
- 数组中存储的是同一类型的元素,可以储存基本数据类型值 — 集合存储的都是对象,而且对象的类型可以不一致
Collection – 单列集合
Collection(I)(集合,是接口)
父接口:Iterable
List(I) -> 顺序结构(数组和链表)ArrayList - 数组LinkedList - 双向链表Vector - 数组Queue(I) -> 队列结构(普通队列)Deque(I) 队列结构(双端队列, 栈)LinkedListSet(I) -> 散列结构(没有顺序)HashSetSortedSet(I)TreeSet - 二叉树
常用API
add、remove、contains、isEmpty、clear
Collection<String> col = new ArrayList<>();// 添加元素col.add(\"haha\");col.add(\"xixi\");col.add(\"heihei\");System.out.println(col); // [haha, xixi, heihei]// 移除指定元素col.remove(\"xixi\");System.out.println(col); // [haha, heihei]// 是否包含指定元素boolean is = col.contains(\"hehe\");System.out.println(is); // false// 是否为空 size() == 0System.out.println(col.isEmpty()); // false// 清空集合col.clear();System.out.println(col); // []
remove(细节问题)
contains也是一样
Collection<Student> col = new ArrayList<>();Student s1 = new Student(\"张老三\", 34);Student s2 = new Student(\"李老六\", 43);col.add(s1);col.add(s2);// [Student{name=\'张老三\', age=34}, Student{name=\'李老六\', age=43}]System.out.println(col);Student s3 = new Student(\"张老三\", 34);col.remove(s3);// 内部调用equals方法判断对象是否一致(equals已修改,只要名字跟年龄相同,则对象就相同)System.out.println(col); // [Student{name=\'李老六\', age=43}]
addAll、containsAll、retainAll、removeAll
(参数是Collection类型)
Collection<String> col1 = new ArrayList<>();col1.add(\"小李飞刀\");col1.add(\"西门吹雪\");Collection<String> col2 = new ArrayList<>();col2.add(\"令狐冲\");col2.add(\"任盈盈\");// 将col2中所有的元素加到col1中col1.addAll(col2);System.out.println(col1); // [小李飞刀, 西门吹雪, 令狐冲, 任盈盈]Collection<String> col3 = new ArrayList<>();col3.add(\"东方不败\");col3.add(\"令狐冲\");// 问col1中是否包含col3中的所有元素boolean b = col1.containsAll(col3);System.out.println(b); // falseCollection<String> col4 = new ArrayList<>();col4.add(\"任我行\");col4.add(\"任盈盈\");col4.add(\"令狐冲\");// 将col1中元素仅保留col4中出现的元素col1.retainAll(col4);System.out.println(col1); // [令狐冲, 任盈盈]Collection<String> col5 = new ArrayList<>();col5.add(\"东方不败\");col5.add(\"令狐冲\");// 将col5中出现过的元素从col1里面移除掉col1.removeAll(col5);System.out.println(col1); // [任盈盈]
toArray(把集合变成数组)
Collection col = new ArrayList();col.add(\"hello\");col.add(3.14);col.add(1);col.add(new Date());// 无参调用toArray (返回值类型永远都是Object[],和泛型无关)Object[] os = col.toArray();for (Object o : os) {System.out.println(o);}
Collection<String> col = new ArrayList();col.add(\"hello\");col.add(\"hi\");/*toArray方法参数: 具体类型的数组, 长度无所谓只是为了提供类型返回值的数组类型, 取决于参数的数组类型返回值的数组长度, 取决于集合的长度*/// 有参调用toArrayString[] ss = col.toArray(new String[0]);for (String s : ss) {System.out.println(s);}
Arrays.asList (把数组变成集合)(返回的是List)
本质上还是数组, 数组长度不可变(不能使用列表的增删方法)
String[] ss = {\"aa\", \"bb\", \"cc\"};// 将数组转换为 List 有序列表List<String> list = Arrays.asList(ss);System.out.println(list);// list真正的类型是Arrays的内部类,不是我们真正的ArrayList// 不能使用增\\删的相关方法/*list.add(\"dd\");*//*System.out.println(list);*/// 让list 变成一个真正的ArrayListList<String> list1 = new ArrayList<>(list);list1.add(\"dd\");System.out.println(list1);
迭代器
只有 next( ) 会移动游标
Collection<String> col = new ArrayList<>();col.add(\"aa\");col.add(\"bb\");col.add(\"cc\");// 没有单独获得某个元素的方法, 只能迭代/遍历 (迭代器)// 获得迭代器时, 游标指向集合中第一个元素之前Iterator<String> it = col.iterator();// 判断下一个元素是否存在while (it.hasNext()) {// 迭代访问(游标指向下一个元素)String s = it.next();// 打印对象System.out.println(s);// 将s对象删除//col.remove(s); // 会出现安全隐患 ConcurrentModificationExceptionit.remove(); // 删除当前游标所在位置的元素(不会改变游标位置)}
迭代器简化:forEach
Collection<String> col = new ArrayList<>();col.add(\"aa\");col.add(\"bb\");col.add(\"cc\");// : 右边 -> 要迭代的集合/数组// : 左边 -> 每一次迭代取出的那个元素// 等同于迭代器中的 String s = it.next()// forEach循环 (只能遍历元素使用, 不能迭代移除)for (String s : col) {System.out.println(s);}// 数组也是可以的int[] arr = {5, 12, 7, 3, 1};for (int a : arr) {System.out.println(a);}
1、List
实现类:ArrayList、LinkedList(特有:addFirst/addLast …)
ArrayList 比 LinkedList 总体效率高,基本上都选择ArrayList
有序列表:可以使用index(允许重复元素)
是Collection的子接口
常用的API(add、remove、set、get、indexOf)
List<String> list = new ArrayList<>();list.add(\"ww\");list.add(\"mm\");// 插入元素list.add(1, \"nn\");System.out.println(list); // [ww, nn, mm]// 删除下标 1 位置上的元素 返回被删除的元素String s2 = list.remove(1);System.out.println(s2); // nnSystem.out.println(list); // [ww, mm]// 改变下标 0 位置上的元素 返回被改变的值String s = list.set(0,\"yy\");System.out.println(s); // ww// 获得下标为 1 的元素String s1 = list.get(1);System.out.println(s1); // mm// 获得字符串 mm 的下标int index = list.indexOf(\"mm\");System.out.println(index); // 1
遍历/迭代:Iterator(迭代器)、 forEach、for
List<String> list = new ArrayList<>();list.add(\"aa\");list.add(\"bb\");list.add(\"cc\");// 1.迭代器遍历Iterator<String> it = list.iterator();while (it.hasNext()) {String s = it.next();System.out.println(s);}// 2.forEach 遍历for(String s : list) {System.out.println(s);}// 3.for 遍历for(int i = 0; i < list.size(); i++) {String s1 = list.get(i);System.out.println(s1);// 删除元素//list.remove(s1); // 继承Collection的remove方法list.remove(i); // List的remove方法i --;}System.out.println(list);
比较器
Collections.sort (List) -> 排序(仅List可以用)
Collections.shuffle(List) -> 打乱
Comparable(重写compareTo)
也是接口
public class Student implements Comparable<Student> {private String name;private Integer age;public Student(String name, int age) {this.name = name;this.age = age;}@Overridepublic String toString() {return \"Student{\" +\"name=\'\" + name + \'\\\'\' +\", age=\" + age +\'}\';}/*** this 和 o 比较* <0 this < o* =0 this = o* >0 this > o*/@Overridepublic int compareTo(Student o) {// 按照年龄排序(升序)return this.age - o.age;// 让方法返回值(降序)//return o.age - this.age;// 按照姓名的字典顺序排序(String有默认重写的compareTo方法)//return this.name.compareTo(o.name);}}
List lstu = new ArrayList<>();lstu.add(new Student(\"lucy\", 23));lstu.add(new Student(\"jack\", 25));lstu.add(new Student(\"tom\", 18));// 排序Collections.sort(lstu);// 排序之后: [Student{name=\'tom\', age=18}, Student{name=\'lucy\', age=23}, Student{name=\'jack\', age=25}]System.out.println(\"排序之后: \" + lstu);
Comparator(重写compare)
也是接口
自定义比较器
List<String> ls = new ArrayList<>();ls.add(\"lucy\");ls.add(\"zhangsan\");ls.add(\"tom\");System.out.println(\"排序之前: \" + ls);// 调用默认的Comparable中的compareTo方法// 字典排序之后: [lucy, tom, zhangsan]Collections.sort(ls);System.out.println(\"字典排序之后: \" + ls);// 自定义比较器进行排序(Comparator)// 按照字符串长度来排序, 长的在前面, 短的在后面Collections.sort(ls, new Comparator<String>() {@Overridepublic int compare(String o1, String o2) {// 降序return o2.length() - o1.length();}});// 长短排序之后: [zhangsan, lucy, tom]System.out.println(\"长短排序之后: \" + ls);
2、Queue
实现类:LinkedList、Stack
常用API
队列:offer、peek、poll
// 1.创建队列 - 先进先出Queue<String> qu = new LinkedList<>();// 2.进队列的方法qu.offer(\"哈哈哈\");qu.offer(\"嘻嘻嘻\");// 3.获取队列头元素的方法String s1 = qu.peek(); // -- 只是查看访问s1 = qu.poll(); // -- 获取队列头元素,并将元素从队列中移除
双端队列:offerFirst、offerLast、pollFirst、pollLast
// 1.创建一个双端队列的方法Deque<String> dq = new LinkedList<>();// 2.拥有队列相关的所有方法 offer peek polldq.offer(\"哈哈哈\");dq.offer(\"嘻嘻嘻\");// 3.可以从队列头和尾加元素dq.offerFirst(\"hahaha\");dq.offerLast(\"heihei\"); // 等同于offer方法// 4.可以从队列头和尾取元素(并删除)String s1 = dq.pollFirst(); // 等同于polls1 = dq.pollLast();
栈:push、pop
Deque(链表栈) — Stack(数组栈)
// Deque当你使用它的 push 和 pop 方法时, 他就变成了栈// 链表栈Deque<String> stack = new LinkedList<>();stack.push(\"a\");stack.push(\"b\");stack.push(\"c\");System.out.println(stack); // [c, b, a]String s = stack.pop();System.out.println(\"栈顶元素: \" + s); // 栈顶元素: cSystem.out.println(\"栈: \" + stack); // 栈: [b, a]// 数组栈Stack<String> stack2 = new Stack<>();stack2.push(\"h\");stack2.push(\"i\");System.out.println(stack2); // [h, i]// 获取栈顶元素String s2 = stack2.pop();System.out.println(\"栈顶: \" + s2); // 栈顶: i
3、Set
实现类:HashSet、TreeSet
没有自己特有的API,和Collection一样
HashSet
散列表/无序的,但不是随机,且不允许重复元素
HashSet容器中,每一个位置都可以存放一个链表
Set<String> set = new HashSet<>();set.add(\"hello\");set.add(\"how\");set.add(\"are\");set.add(\"you\");// 不允许重复set.add(\"you\");// [how, are, hello, you]System.out.println(set);
remove原理
hashCode和equals要一起重写
hashCode()
1、没有重写, 所有对象的哈希值都不一样
2、哈希值不一样, 定位到的set中的位置, 可能相同
Set<Student> set = new HashSet<>();set.add(new Student(\"张三\", 12));set.add(new Student(\"李四\", 34));set.add(new Student(\"张三\", 12));set.add(new Student(\"李四\", 34));System.out.println(set);System.out.println(set.size());/*** 1.Student没有重写equals和hashCode, 添加进4个元素* 2.重写了equals方法, 但是没有重写hashCode, 添加进4个元素* 3.重写了hashCode方法, 但是没有重写equals, 添加进4个元素* 4.重写了equals和hashCode方法, 添加进2个元素*/
@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Student student = (Student) o;return age == student.age &&Objects.equals(name, student.name);}@Overridepublic int hashCode() {return Objects.hash(name, age);}
Student s1 = new Student(\"张三\", 12);// 属性值发生了改变s1.setAge(21);// 移除的时候,重新计算位置,发现位置上找不到s1对象,不会移除set.remove(s1);
TreeSet
不允许重复,取决于 compare 或者 compareTo 方法(和equals方法无关)
Set<String> set = new TreeSet<>();set.add(\"tom\");set.add(\"jack\");set.add(\"zhangsan\");set.add(\"lisi\");set.add(\"lisi\");System.out.println(set); // [jack, lisi, tom, zhangsan]
// 可以通过构造方法, 传入比较器Set<String> set = new TreeSet<>(new Comparator<String>() {@Overridepublic int compare(String o1, String o2) {return o1.length() - o2.length(); // 比较长度(长度相等则判断为是同一个对象)}});set.add(\"tom\");set.add(\"jack\");set.add(\"zhangsan\");set.add(\"lisi\");set.add(\"lisi\");System.out.println(set); // [tom, jack, zhangsan]