毕老师视频 day14-day17
1.集合类:
面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储,集合就是存储对象最常用的一种方式。
- 存储方式有:数组、集合类2种
数组和集合类都是容器,不同之处:
- 数组长度是固定的,集合长度是可变的。
- 数组可以存储基本数据类型,集合只能存储对象。
集合类的特点:
- 只用于存储对象; 集合长度是可变的; 集合可以存储不同类型的对象。
为什么会出现很多容器:
- 因为每一个容器对数据的存储方式都不同, 这个存储方式就是数据结构。
2. 集合框架
Collection接口的共性方法:
- add方法的参数类型是Object,以便接收任意类型的对象;
- 集合中存储的都是对象的引用。
//创建一个集合容器,使用Collection接口的子类 ArrayList ArrayList al = new ArrayList(); al.add("212334"); al.add("japa"); al.add("212"); al.add("japa");//1.追加 System.out.println(al); System.out.println(al.size()); //2.大小 al.remove("japa"); //3.删除 System.out.println(al); System.out.println(al.contains("japa"));//4.查询 System.out.println(al.isEmpty()); //5.空否 // System.out.println(al.clear()); //6.清空 System.out.println(al); ArrayList bl = new ArrayList(); bl.add("japa"); bl.add("japa1"); bl.add("japa2"); // System.out.println(al.retainAll(bl)); //al保留交集部分,bl不变 System.out.println(al.removeAll(bl)); //al移除交集部分,bl不变 System.out.println(al); System.out.println(bl);
3. 迭代器:
- 集合的取出元素的方式。
取出方式被定义成了集合的内部类,这样它就可以直接访问集合内容的元素。
每个容器的数据机构不同,所以取出方式的细节也不同,但是有共性内容: 判断和取出
这些内部类都符合一个规则: 接口 Iterator
然后通过集合获取取出方式的对象? 通过每个集合类定义的 iterator() 方法
接口Iterator 只有3个方法:
boolean hasNext() // 如果仍有元素可以迭代,则返回 true。
E next() //使迭代器移至下一个位置,返回迭代器经过的对象
void remove() //删除迭代器左边的对象
Iterator it = al.iterator(); System.out.println(it.hasNext()); System.out.println(it.next()); //返回第一个对象, it.remove(); //删除第一个对象 System.out.println(it.next()); //返回第二个对象 System.out.println(al);
4. Collection接口的2个常见子接口: List接口 Set接口
List 元素是有序的,元素可以重复。因为该集合体系有索引。
Set 元素是无序的,元素不可以重复。
List集合特有方法:
//增 void add(int index, E element) boolean addAll(int index, Collection<? extends E> c) //删 E remove(int index) //返回被删掉的数据 //改 E set(int index, E element) //返回被替代的数据 //查 E get(int index) List<E> subList(int fromIndex, int toIndex) ListIterator<E> listIterator(int index)
int indexOf(Object o)
List集合特有的迭代器: ListIterator
在迭代时,不可以通过集合对象的方法操作集合中的元素,会发生异常。 所以只能用迭代器的方法操作元素。
Iterator提供的方法有限,如果需要添加、修改操作,需要子接口 ListIterator。
void add(E e) //加到迭代器当前的位置,并使其右移一位 boolean hasNext() boolean hasPrevious() E next() //迭代器右移一位,返回经过的对象 E previous() //迭代器左移一位,返回经过的对象 int nextIndex() //迭代器不动,返回迭代器右边的角标 int previousIndex() void remove() //移除由 next 或 previous 返回的最后一个元素 void set(E e) //替换由 next 或 previous 返回的最后一个元素 lit.next(); //迭代器在第一个与第二个之间 //lit.set("不是"); lit.remove(); //移除第一个对象 System.out.println(al); lit.next(); lit.next(); lit.previous(); //迭代器也在第一个与第二个之间 //lit.set("不是"); lit.remove(); //移除第2个对象 System.out.println(al);
5. List接口有3个常见的子类对象: ArrayList LinkedList Vector
- ArrayList 底层数据结构使用的数组结构: 查询速度快,增删速度慢。 线程不同步
- LinkedList 底层数据结构使用的链表结构: 查询速度慢,增删速度快。 线程不同步
- Vector 底层是数组数据结构, 是线程同步的。 1.0有的,1.2开始被ArrayList替代
(ArrayList 默认长度10,超出后延迟50%; Vector默认长度10,超出后延迟100%)
/* 枚举是Vector特有的取出方式。1.0时只有这种方式。 因为枚举名称过长,已经被迭代器取代了 */ Vector v = new Vector(); v.add("kaka1"); v.add("kaka1"); v.add("kaka1"); v.add("kaka1"); for( Enumeration enu = v.elements();enu.hasMoreElements();) System.out.println(enu.nextElement());
6. LinkedList 特有方法:
void addFirst(E e) void addLast(E e) //将指定元素添加到此列表的结尾。 E getFirst() //返回此列表的第一个元素。 E getLast() //返回此列表的最后一个元素。 E removeFirst() //返回并移除此列表的第一个元素。 E removeLast() //返回并移除此列表的最后一个元素。没有元素抛出异常 //JDK1.6版本后出现了替代方法,不再抛出异常 boolean offerFirst(E e) //在此列表的开头插入指定的元素。 boolean offerLast(E e) //在此列表末尾插入指定的元素。 E peekFirst() //获取但不移除此列表的第一个元素;如果此列表为空,则返回 null。 E peekLast() //获取但不移除此列表的最后一个元素;如果此列表为空,则返回 null。 E pollFirst() //获取并移除此列表的第一个元素;如果此列表为空,则返回 null。 E pollLast() //获取并移除此列表的最后一个元素;如果此列表为空,则返回 null。
7. Set集合的方法和 Collection 是一致的。
HashSet底层数据结构是哈希表。 线程不同步。 HashSet如何保证元素的唯一性?:
- 通过元素Object的方法 hashCode() 和 equals() 来完成。
- 先判断hash值是否相等,如果哈希值相同,才会调用 equals 方法。
- 因此,对应自定义对象,通常要重写hashCode 和 equals 方法。
- HashSet的寻找和删除方法,contains(obj) remove(obj) 依赖的也是元素的hashCode和equals方法。
- ArrayList判断元素是否相同只调用equals().
TreeSet 可以对Set集合中的元素进行排序。 底层数据结构是二叉树。 如何保证元素唯一性?:
- compareTo 方法。 contains(obj)、 remove(obj)也调用的compareTo方法。
- TreeSet排序的第一种方式: 让元素具备可比较性, 元素需要实现 Comparable 接口,重写 compareTo 方法。
- 第二种方式: 元素不具备比较性时,需要让集合自身具备比较性。
- 需要定义比较器,将比较器对象作为参数传递给TreeSet集合的构造函数
- 当2种比较方式都存在时,会自动调用比较器方法。
class Person implements Comparable { private String name; private int age; Person(String name, int age) { this.name = name; this.age = age; } public int compareTo(Object obj) { System.out.println("比较"); if(!(obj instanceof Person)) throw new RuntimeException("对象类型错误"); Person p = (Person) obj ; if(this.age>p.age) return 1; if(this.age==p.age) { return this.name.compareTo(p.name); } return -1; } } class myCompare implements Comparator { public int compare(Object o1,Object o2) { Person p1 = (Person) o1; Person p2 = (Person) o2; return 1; } }
8. 泛型: JDK1.5之后出现的新特性,用于解决安全问题,是一个安全机制。 好处:
- 将运行时期可能出现的ClassCastException异常转移到了编译时期,方便程序员解决问题
- 避免了强制类型转换。
泛型格式: 通过 <> 来定义要操作的引用数据类型。
什么时候使用泛型??
- 通常在集合框架中很常见,只要见到<>就要定义泛型。
- 在使用集合时,将集合要存储的数据类型作为参数传递到<>中即可。
- 接口 Comparable 、 Comparator 都带泛型。
9.泛型类:
class Util<T> //泛型类 { private T t; public void setObject(T t) { this.t = t; } public T getObject() { return t; } }
什么时候定义泛型?
- 当类中要操作的引用数据的类型不确定时,
- 早期用Object来完成扩展,现在用泛型来完成扩展。
泛型方法:
- 泛型类定义的泛型,在整个类都有效
- 当泛型类的实例指出了要操作的数据类型,所有方法的能操作的数据类型就固定了。
- 为了让不同的方法可以操作不同类型的不确定数据, 可以把泛型定义在方法上
class Util <QQ> //可以在泛型类中定义泛型方法表示不同泛型 { private QQ q; public <T> void setObject(T t) { } //泛型的声明,必须在方法的修饰符(public,static,final,abstract等)之后,返回值声明之前。 //和泛型类一样,可以声明多个泛型,用逗号隔开。 public static <T,K> T getObject(K key) { return t; } }
静态方法泛型:
- 静态方法不可以访问类上定义的泛型
- 静态方法只能使用方法自身上定义的泛型
class Util<T> //泛型类 { private T t; public static <K> void method(K x) { System.out.println(x); } }
泛型定义在接口上:
interface Inter <T> { void show(T t); } class Demo1 implements Inter<String> { public void show(String str) { System.out.println(str); } } class Demo2<T> implements Inter<T> //2个T { public void show(T t) { System.out.println(t); } } class Main { public static void main(String [] args) { Demo1 dm1 = new Demo1(); dm1.show("haha"); Demo2<Integer> dm2 = new Demo2<Integer>(); dm2.show(007); } }
? 通配符
泛型的限定:
- ? extends E :可以接收E类型或者E的子类型, 上限
- ? super E : 可以接收E类型或者E的父类型 , 下限
class Person { private String name; Person(String str) { this.name = str; } public String getName() { return name; } } class Student extends Person { Student(String str) { super(str); } } class Woker extends Person { Woker(String str) { super(str); } } class Comp implements Comparator<Person> //super泛型限定,可以比较Person的子类 { public int compare(Person s1, Person s2) { return s1.getName().compareTo(s2.getName()); } } class Main { public static void main(String [] args) { //super泛型限定,定义比较器时,可以定义Student或者它的父类 TreeSet<Student> ts = new TreeSet<Student>(new Comp()); ts.add(new Student("学生11")); TreeSet<Person> ts2 = new TreeSet<Person>(new Comp()); ts2.add(new Person("人11")); ArrayList<Person> ala = new ArrayList<Person>(); ala.add(new Person("人1")); ala.add(new Person("人2")); printAl(ala); ArrayList<Student> alb = new ArrayList<Student>(); alb.add(new Student("学生1")); alb.add(new Student("学生2")); printAl(alb); ArrayList<Woker> alc = new ArrayList<Woker>(); alc.add(new Woker("工人1")); alc.add(new Woker("工人2")); printAl(alc); } //extends泛型限定,定义参数时,可以定义Person或者它的子类 public static void printAl(ArrayList<? extends Person> al) { for(Iterator<? extends Person> it=al.iterator(); it.hasNext();) { System.out.println(it.next().getName()); } } }
10. Map集合
- Map集合表示映射关系, 键-->值, 键不可重复
//添加 V put(K key, V value) // 在映射中添加传入的键和值, 如果映射中已经包含了一个该键的映射,则旧值被替换。 void putAll(Map<? extends K,? extends V> m) //从指定映射中将所有映射关系复制到此映射中 //删除 V remove(Object key) //如果存在一个键的映射,则将其从此 Map 中移除。 void clear() //从此映射中移除所有映射关系。 //查询 boolean containsKey(Object key) //如果此映射包含指定键的映射关系,则返回 true。 boolean containsValue(Object value) //如果此映射将一个或多个键映射到指定值,则返回 true。 boolean isEmpty() //如果此映射未包含键-值映射关系,则返回 true。 //获取 V get(Object key) // 返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null。 int size() //返回此映射中的键-值映射关系数。 Collection<V> values() //返回此映射中包含的值的 Collection 视图。 Set<Map.Entry<K,V>> entrySet() //返回此映射中包含的映射关系的 Set 视图。 Set<K> keySet() // 返回此映射中包含的键的 Set 视图。
|-- Hashtable 底层数据结构是哈希表, 不可以存入null , 线程同步。
- 键必须实现HashCode和equals方法
|-- HashMap 底层数据结构也是哈希表, 允许使用 null 键、 null 值, 线程不同步。
|-- TreeMap 底层数据结构是红黑树。 线程不同步
- 可以用于给map集合中的键进行排序
Set集合的底层实现使用了Map集合
11. Map集合有2种取出方式:
- keySet 将Map中所有的键存入Set集合,使用Set集合的迭代器取出键,再根据get(key)方法取出值
- entrySet 将Map集合中的映射关系存入 Set集合,使用Set集合的迭代器取出关系。这个关系的数据类型是: Map.Entry
- Map.Entry 是Map 接口的内部接口
class Student implements Comparable<Student> { private String name; private int age; Student(String name, int age) { this.name = name; this.age = age; } public int hashCode() { return name.hashCode()+age*33; } public boolean equals(Object obj) { if(!(obj instanceof Student)) throw new RuntimeException("类型异常"); Student stu = (Student) obj; return this.name.equals(stu.name) && this.age==stu.age; } public int compareTo(Student stu) { if(this.name.compareTo(stu.name)!=0) return this.name.compareTo(stu.name); return this.age-stu.age; } public String toString() { return name+"::"+age; } } class Main { public static void main(String [] args) { HashMap<Student,String> hm = new HashMap<Student,String>(); hm.put(new Student("any",21),"beijing"); hm.put(new Student("boy",29),"fffkk"); hm.put(new Student("gir",25),"zhongng"); hm.put(new Student("zip",18),"gasotan"); //第一种取出方式 Set<Student> keyset = hm.keySet(); for(Iterator<Student> it=keyset.iterator(); it.hasNext();) { Student stu = it.next(); String adr = hm.get(stu); System.out.println(stu+"--"+adr); } //第二种取出方式 Set<Map.Entry<Student,String>> es = hm.entrySet(); for(Iterator<Map.Entry<Student,String>> it=es.iterator(); it.hasNext();) { Map.Entry<Student,String> me = it.next(); Student stu = me.getKey(); String adr = me.getValue(); System.out.println(stu+"==="+adr); } } }
12.Collections
- Collections是对集合框架的一个工具类。它里边的方法都是静态的,不需要创建对象。并未封装特有数据。
- 在Collections工具类中大部分方法是用于对List集合进行操作的,如比较,二分查找,随机排序等。
Collections和Collection的区别:
- Collection是集合框架中的一个顶层接口,它里面定义了单列集合的共性方法。
它有两个常用的子接口:
List:对元素都有定义索引。有序的。可以重复元素。
Set:不可以重复元素。无序
- Collections是集合框架中的一个工具类。该类中的方法都是静态的。提供的方法中有可以对list集合进行排序,二分查找等方法
通常常用的集合都是线程不安全的。因为要提高效率。如果多线程操作这些集合时,可以通过该工具类中的同步方法,将线程不安全的集合,转换成安全的。
//查找 T max(Collection<? extends T> coll);//根据集合的自然顺序,获取coll集合中的最大元素 T max(Collection<? extends T> coll,Comparator<? super T> comp);//根据指定比较器comp的顺序,获取coll集合中的最大元素 static <T> int binarySearch(Lsit<? extends Comparable<? super T>> list,T key);//二分法搜索list集合中的指定对象 //替换 voidfill(List<? super T> list, T obj);//将list集合中的全部元素替换成指定对象obj boolean replaceAll(List<T> lsit,T oldVal,T newVal);//用newVal替换集合中的oldVal值 void swap(Listlist,int i,int j);/在指定列表的指定位置处交换元素 //排序: void shuffle(List<?> list);//使用默认随机源对list集合中的元素进行随机排序 void sort(Lsit<T> list);//根据自然顺序对list集合中的元素进行排序 void sort(List<T> lsit,Comparator<? super T> c);//根据指定比较器c的排序方式对list集合进行排序 //反转 reverse(List<?> list);//反转list集合中元素的顺序 Comparator reverseOrder();//返回一个比较器,强行逆转了实现Comparable接口的对象的自然顺序 Comparator reverseOrder(Comparator<T> cmp);//返回一个比较器,强行逆转了指定比较器的顺序 //同步的集合 List<T> synchronizedList(List<T> list);//返回支持的同步(线程安全的)List集合 Map<K,V> synchronizedList(Map<K,V> m); //返回支持的同步(线程安全的)Map集合
13. Arrays
- Arrays是用于操作数组的工具类。里边的方法也全是静态的。不需要创建对象。
- 把数组变成List集合的好处:可以使用集合的思想和方法来操作数组中的元素。如:contains,get,indexOf,subList等方法。
方法:
Lsit<T> asList (T... a);//将数组转换为集合,返回一个受指定数组支持的固定大小的列表
- 将数组转换成集合,不可使用集合的增删方法,因为数组的长度是固定的。如果进行增删操作,则会产生UnsupportedOperationException的编译异常。
- 如果数组中的元素都是对象,则变成集合时,数组中的元素就直接转为集合中的元素。
- 如果数组中的元素都是基本数据类型,那么会将该数组作为集合中的元素存在。
binarySearch()//二分查找方法 fill() //替换方法 sort() //排序方法等
集合也可以转换成数组:
Collection接口中的toArray方法。
<T> T[] toArray(T[] a) //将集合变为指定类型的数组。
- 指定类型的数组到底要定义多长呢?
当指定类型的数组长度小于了集合的size,那么该方法内部会创建一个新的数组。长度为集合的size。
当指定类型的数组长度大于了集合的size,就不会新创建了数组。而是使用传递进来的数组。
所以创建一个刚刚好的数组最优。
- 为什么要将集合变数组?
为了限定对元素的操作。不需要进行增删了。
14. 高级for循环:
for(数据类型变量名 :被遍历的集合(collection)或者数组) {执行语句}
特点:
- 对集合进行遍历。只能获取集合元素。但是不能对集合进行操作。可以看作是迭代器的简写形式。
- 迭代器除了遍历,还可以进行remove集合中元素的动作。如果使用ListIterator,还可以在遍历过程中对集合进行增删改查的操作。