集合知识回顾:
集合类的特点:提供了一种存储空间可变的存储模型,存储的数据容量可以随时发生改变
1.2 集合类体系结构
| 赵童|
| 曲波 | 这种数据是单列的,一般都是用collection集合来存储的
| 曲波|
|1|张三 |
|2|李四| 这种双列的数据一般是通过Map集合来存储的
| 3 |王五 |
而在collection集合下,有重复的数据一般都是通过collection下的List来存储的,比如:
| 李四|
|李四 |
|张三 |
加入我们还是存储上面的这个集合,但得到的数据是:
| 张三 |
| 李四| 这样的结果,第二个张三在集合中没有存储成功,一般是用Set集合来存储的,
这些集合都是接口
重点学习:
1、3 Collection 集合概述和使用
!collection集合概述:
!是单例集合的顶层接口,它表示一组对象,这些对象也称为Collection的元素
!JDK不提供此接口的任何实现类,它提供更具体的子接口(如:Set 和List)实现。
创建Collection集合的对象:
!多态的方式
!具体的实现类ArrayList()
/**
* 创建Collection集合的对象
* 多态的方式
* ArrayList()
* 重写了toString的方法
*/
public class CollectionDemo1 {
public static void main(String[] args) {
Collection<String> c= new ArrayList<>();
//添加元素:boolean add (E e)
c.add("hello");
c.add("word");
c.add("java");
System.out.println(c);
}
}
1、4 Collection 集合常用的方法
boolean add(E e):添加元素
boolean remove(Object o):从集合中移除指定的元素
void clear:清空集合中的元素
boolean contains(Object o):判断集合中是否存在指定的元素
boolean idEmpty():判断集合是否为空
int size():集合的长度,也就是集合中元素的个数
/**
* boolean add(E e):添加元素
* boolean remove(Object o):从集合中移除指定的元素
* void clear:清空集合中的元素
* boolean contains(Object o):判断集合中是否存在指定的元素
* boolean idEmpty():判断集合是否为空
* int size():集合的长度,也就是集合中元素的个数
*
* alt + 7可以打开一个窗口,能看到类的全部信息
*/
public class CollectionDemo2 {
public static void main(String[] args) {
Collection<String> collection = new ArrayList<String>();
// System.out.println( collection.add("hello"));
// System.out.println( collection.add("world"));
// System.out.println( collection.add("world"));
// System.out.println(collection);
collection.add("hello");
collection.add("world");
collection.add("java");
// System.out.println(collection.remove("hello"));
//boolean contains(Object o):判断集合中是否存在指定的元素
// System.out.println(collection.contains("java"));
// collection.clear();
// System.out.println(collection.contains("javaEE"));
//boolean idEmpty():判断集合是否为空
// System.out.println(collection.isEmpty());
//int size():集合的长度,也就是集合中元素的个数
System.out.println(collection.size());
System.out.println(collection);
}
}
1、5 Collection 集合的遍历
iterator:迭代器,集合的专用遍历方式
!Iterator iterator():返回此集合中的元素迭代器,通过集合的iterator()方法得到
!迭代器是通过集合的iterator()方法得到的,所以我们说它是以来于集合二存在的
Iterator中常用的方法:
!E next():返回迭代器中的下一个元素
!boolean hasNext():如果迭代器具有更多元素,则返回true
/**
* iterator:迭代器,集合的专用遍历方式
* !Iterator<E> iterator():返回此集合中的元素迭代器,通过集合的iterator()方法得到
* !迭代器是通过集合的iterator()方法得到的,所以我们说它是以来于集合二存在的
* Iterator中常用的方法:
* !E next():返回迭代器中的下一个元素
* !boolean hasNext():如果迭代器具有更多元素,则返回true
*/
public class IteratorDemo {
public static void main(String[] args) {
Collection<String> collection = new ArrayList<String>();
collection.add("hello");
collection.add("world");
collection.add("java");
//!Iterator<E> iterator():返回此集合中的元素迭代器,通过集合的iterator()方法得到
Iterator<String> iterator = collection.iterator();
/*
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
......
}
*/
//E next():返回迭代器中的下一个元素
// System.out.println(iterator.next());
// System.out.println(iterator.next());
// System.out.println(iterator.next());
// System.out.println(iterator.next());//NoSuchElementException:表示该请求的元素不存在
// System.out.println(collection);
//boolean hasNext():如果迭代器具有更多元素,则返回true
// if (iterator.hasNext()){
// System.out.println(iterator.next());
// }
// if (iterator.hasNext()){
// System.out.println(iterator.next());
// }
// if (iterator.hasNext()){
// System.out.println(iterator.next());
// }
// if (iterator.hasNext()){
// System.out.println(iterator.next());
// }
//用while循环改进
while (iterator.hasNext()){
// System.out.println(iterator.next());
String s = iterator.next();
System.out.println(s);
}
}
}
1、6 集合中的使用步骤
1、创建集合对象
2、添加元素
2、1 创建元素
2、2 添加元素到集合
3、遍历集合:
3、1通过集合对象获取迭代器
3、2通过迭代器的hasNext()方法判断是否还有元素
3、3通过迭代器对象的next()方法获取到下一个元素
2、 List
2、1 List 集合概述的特点
List集合概述
!有序集合(也称为序列),用户可以精确控制每个元素的插入位置,用户可以通过索引来访问元素。
!与Set集合不同的是,列表允许有重复元素
List集特点:
!有序:存储和取出的元素顺序一致
!可重复:存储的元素可以重复
2、2List集合特有的方法
void add(int index,E element):在此集合中的指定位置插入指定的元素
E remove(int index):删除指定索引处的元素,返回被删除的元素
E set(int index,E element):修改指定位置处的元素,返回被修改的元素
E get(int index):返回指定索引处的元素
/**
* void add(int index,E element):在此集合中的指定位置插入指定的元素
* E remove(int index):删除指定索引处的元素,返回被删除的元素
* E set(int index,E element):修改指定位置处的元素,返回被修改的元素
* E get(int index):返回指定索引处的元素
*/
public class ListDemo1 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
list.add("java");
list.add(1,"唐某");
// list.add(11,"唐某"); //索引越界
// list.remove(2);
// list.remove(11);
// String 去比 = list.set(1, "去比");
// System.out.println(list.get(1));
// System.out.println(list);
//遍历方式1:迭代器
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
String s = iterator.next();
System.out.println(s);
}
//遍历方式2:索引
for (int i =0 ;i< list.size();i++){
String s = list.get(i);
System.out.println(s);
}
}
}
2、3并发修改异常
public class ListDemo4 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
list.add("java");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
String s = iterator.next();
if (s.equals("world")){
list.add("javaee");
}
}
System.out.println(list);
}
}
代码逻辑没有问题,但会抛出:Exception in thread “main” java.util.ConcurrentModificationException 并发修改异常。
public interface List<E> extends Collection<E> {
Iterator<E> iterator();
boolean add(E e);
}
public abstract class AbstractList<E>{
protected int modCount = 0;
}
public class ArrayList<E> extends AbstractList<E>implements List<E>{
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
int expectedModCount = modCount;
/*
modCount:实际修改集合的次数
expectedModCount:预期修改集合的次数
*/
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
final void checkForComodification() {
if (modCount != expectedModCount)//modCount:修改集合的次数;expectedModCount:预期修改集合的次数
throw new ConcurrentModificationException();
}
}
}
上面是源码,导致的原因是因为预期修改集合次数与实际修改集合次数不相等导致的。
正确代码如下:
public class ListDemo4 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
list.add("java");
Iterator<String> iterator = list.iterator();
// while (iterator.hasNext()){
// String s = iterator.next();
// if (s.equals("world")){
// list.add("javaee");
// }
// }
for (int i = 0; i < list.size(); i++) {
String s = list.get(i);
if (s.equals("world")){
list.add("麻瓜");
}
}
System.out.println(list);
}
}
产生原因:
!迭代器遍历过程中,通过集合对象修改了集合中元素的长度,造成了集合元素的常速,造成了迭代器获取元素中判断预期修改值和实际修改值不一致导致的。
解决方案:
!用for循环遍历,然后用集合对象做对应的操作即可。
2、4 ListIterator
ListIterator:列表迭代器
!通过List集合的listIterator()方法得到,所以说它是List集合特有的迭代器。
沿任意方向遍列表的列表迭代器,在迭代期间可以修改列表,并获取列表中迭代器的当前位置。
ListIterator中的常用方法:
E next():返回迭代中的下一个元素
boolean hasNext():如果迭代具有更多的元素,则返回true
E previous():返回迭代器中的上一个元素
boolean hasPrevious():如果列表迭代器在相反的方向遍历列表时具有更多的元素,则返回true;
void add(E e):将指定的元素插入到列表当中;
public class ListIteratorDemo1 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
list.add("java");
// 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);
// }
//获取列表迭代器
ListIterator<String> listIterator = list.listIterator();
while (listIterator.hasNext()){
String s = listIterator.next();
if (s.equals("hello")){
listIterator.add("曲波");
}
}
System.out.println(list);
}
}
2、5增强for循环
增强for:简化数组和Collection集合的遍历
!实现iterab接口的类允许其对象成为增强for语句的目标,因为
所以Collection接口下的所有都可以用增强for
!它是JDK5之后才出现的,其内部原理就是Iterator迭代器。
增强for的格式:
for(元素数据类型 变量名:数组或者Collection集合){
//在此处使用变量即可,该变量就是元素
}
public class ForTest {
public static void main(String[] args) {
int[] arrs={1,2,3,4,5};
for (int arr:arrs) {
System.out.println(arr);
}
System.out.println("-----");
List<String> list = new ArrayList<>();
list.add("hello");
list.add("java");
list.add("曲波");
for (String name:list) {
System.out.println(name);
}
//内部是一个Iterator迭代器
for (String name:list) {
if (name.equals("hello")){
list.add("赵童");
}
}
}
}
由此证明增强for的底层就是Iterator迭代器
如何选取三种遍历方式?
1、当需要使用索引的时候只能用普通方式;
2、当你只是为了查找元素就是使用增强for;
2、6数据结构
数据结构:
数据结构是计算机存储、组织数据的方式。是指相互之间存在一种或多种特定关系的数据元素的集合;通常情况下精心选择的数据结构可以带来更高的允许或者存储效率。
2、7 常见数据结构:栈
!数据进入到栈模型的过程叫做:压/进栈
!数据离开栈模型的过程叫做:弹/出栈
栈是一种先进后出的模型
2、8 常见数据结构:队列
数据从后端进入队列模型的过程称为:入队列
数据从前端离开队列模型的过程称为:出队列
队列是一种先进先出的模型
2、9常见数据结构:数组
查询数据通过索引定位,查询任意数据耗时相同,查询速度快;
删除数据时,要将原始数据删除,同时后面每个数据前移,删除效率慢
添加数据时,添加位置后的每个元素后移,再添加元素,添加效率极低;
2、10常见数据结构:链表
链表的每个元素称为:结点;
如何在链表A和C之间添加一个结点B?
将A的下一个结点地址改为B的地址,再将B的下一个结点地址改为C的当前地址;
如何删除链表A和C之间的数据B呢?
将A的下一个结点地址指向C的地址,然后再将B删除
链表是一种增删快的模型(对比数组)
查询D是否存在必须从头head)开始查询
查询第三个数据,也必须从(head)开始查询。
2、11 List集合子类特点
List集合常用子类:ArrayList,LinkedList
!ArrayList:底层数据结构是数组,查询快,增删慢;
!LinkedList:底层数据结构是链表,查询慢,增删快;
2、12 LinkedList 集合的特有功能
public void addFirst(E e):在该列表开头插入指定的元素;
public void addLast(E e):将指定的元素追加到此列表的末尾;
public E getFirst():返回此列表中的第一个元素;
public E getLast():返回列表中的最后一个元素;
public E removeFirst():从列表中删除并返回第一个元素;
public E removeLast():从列表中删除并返回最后一个元素;
3、Set
Set集合特点:
!不包含重复元素的集合;
!没有带索引的方法,所以不能使用普通for遍历循环
HashSet:对集合的迭代顺序不做任何保证,不能为null值
public class SetDemo {
public static void main(String[] args) {
Set<String> set = new HashSet<>();
set.add("hello");
set.add("world");
set.add("java");
set.add("java");
//System.out.println(set);
for (String s:set) {
System.out.println(s);
}
}
}
3、2 哈希值
哈希值:是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值
Object类中有一个方法可以获取对象的哈希值
!public int hashCode():返回对象的哈希值
对象的哈希值特点
!同一个对象多次调用hashCode()方法返回的哈希值是相同的
!默认情况下,不同对象的哈希值是不同的。而重写hashCode()方法,可以实现让不同对象的哈希值相同
3、3HashSet集合的概述和特点
HashSet集合特点:
!底层数据结构是哈希表
!对集合的迭代顺序不作任何保证,也就是说不保证存储和取出的元素一致
!没有索引的方法,所以不能使用普通for循环遍历
!由于是Set集合,所以没有重复元素;
3、4 HashSet集合保证元素唯一性的源码分析
HashSet<String> hashSet = new HashSet<>();
hashSet.add("hello");
hashSet.add("world");
hashSet.add("java");
hashSet.add("java");
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
3、5 常见数据结构:哈希表
哈希表:
!JDK8之前,底层采用数组+链表实现,可以说是一个元素为链表的数组
!JDK8之后,在长度较长的时候,底层实了优化
3、6 LinkedHashset集合概述和特点
LinkedHashSet集合的特点:
!哈希表和链表实现的Set接口,具有可预测的迭代次序
!由链表保证保证元素有序,也就是说元素的存储和取出顺序是一致的;
!有哈希表保证元素唯一,也就是说没有重复元素
public class Test1 {
public static void main(String[] args) {
LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add("hello");
linkedHashSet.add("java");
linkedHashSet.add("world");
linkedHashSet.add("world");
for (String i:linkedHashSet) {
System.out.println(i);
}
}
}
3、7 TreeSet集合概述和特点
TreeSet集合的特点
!元素有序,这里的顺序不是指存储顺序,而是按照一定的规则进行排序,具体排序方式取决于构造方法
@TreeSet():根据其元素的自然排序进行排序,就是按照元素的大小;
@TreeSet(Comparator comparator):根据指定的比较器进行排序
!没有带索引的方法,所以不能使用普通for循环遍历
!由于是Set集合,所以不能包含重复的元素
public class TreeSetTest {
public static void main(String[] args) {
TreeSet<Integer> treeSet = new TreeSet<>();
treeSet.add(1);
treeSet.add(11);
treeSet.add(10);
treeSet.add(1111);
treeSet.add(11);
for (Integer num:treeSet) {
System.out.println(num);
}
}
}
3、8 自然排序Comparable 的使用
要求:存储学生对象并遍历,创建TreeSet集合使用午餐构造方法;
从年龄从小到大排序,年龄相同时,按照名字的字母顺序排序;
此接口强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的 compareTo 方法被称为它的自然比较方法。
补充:三目运算符
num0?a>b:a<b;
代表num0为真允许a>b,否则就是a<b;
Student:
public class Student implements Comparable<Student>{
private Integer id;
private String name;
public Student() {
}
public Student(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
if (id != null ? !id.equals(student.id) : student.id != null) return false;
return name != null ? name.equals(student.name) : student.name == null;
}
@Override
public int hashCode() {
int result = id != null ? id.hashCode() : 0;
result = 31 * result + (name != null ? name.hashCode() : 0);
return result;
}
@Override
public int compareTo(Student s) {
// return -1;
// return 1;
// return 0;
int i = this.id - s.id;//this代表先加进来的那个元素
int num= i==0?this.name.compareTo(s.name):i;
return num;
}
}
测试:
public class TreeSetDemo1 {
public static void main(String[] args) {
Student student = new Student(22,"tang");
Student student2 = new Student(55,"zhang");
Student student3 = new Student(50,"mu");
Student student1 = new Student(50,"qu");
TreeSet<Student> treeSet= new TreeSet<>();
treeSet.add(student);
treeSet.add(student3);
treeSet.add(student2);
treeSet.add(student1);
for (Student s:treeSet) {
System.out.println(s);
}
}
}
结论:
用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序的;
自然排序,就是让元素所属的类实现Comparable接口,重写compareTo(T o)方法;
重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写;
3、9 比较器排序Comparator的使用
!存储同学对象并遍历,创建TreeSet集合使用带参构造
!要求:按照年龄从小到大,年龄相同时,按照名字的字母顺序排序
public class TreeSetDemo2 {
public static void main(String[] args) {
//创建集合对象
TreeSet<Student> treeSet = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
int num = o1.getId()-o2.getId();
int num1= num == 0 ? o1.getName().compareTo(o2.getName()):num;
return num1;
}
});
Student student = new Student(22,"tang");
Student student2 = new Student(55,"zhang");
Student student3 = new Student(50,"mu");
Student student1 = new Student(50,"qu");
treeSet.add(student);
treeSet.add(student3);
treeSet.add(student2);
treeSet.add(student1);
for (Student s:treeSet) {
System.out.println(s);
}
}
}
结论:
!用TreeSet集合存储自定义对象,带参构造方式使用的是比较器排序对元素进行排序的。
!比较器排序,就是让集合构造方法接收Comparator的实现类对象,重写compare(T t1,T t2)方法;