一、容器的概念
二、容器API
特点:
1、可以存放不同类型的数据,二数组只能存放固定类型的数据
2、使用Arraylist子类实现的时候,初始化的长度是10,当长度不够的时候会进行自动扩容操作
api方法:
1、add:要求必须传入的参数是Object对象,因此写入基本数据类型的时候,包含了自动拆箱和自动装箱的过程
2、addAll:添加另一个集合的元素到此集合中
3、clear:清空集合的内容,但是集合对象不会被回收
4、contains:判断此集合中是否包含指定的元素
5、containsAll:判断此集合中是否包含另一个集合
6、equals:也是比较集合里面的元素是否相等
7、isEmpty:判断集合是否为空
8、remove:删除集合中的一个元素
9、removeAll:删除此集合中包含的另一个集合的元素
10、removeIf:判断此集合是否拥有另一个集合的所有元素
11、size:返回当前结合的元素个数
12、toArray:将集合转换成数组
示例
package com.msbline.basic.collectionPkg;
import java.util.ArrayList;
import java.util.Collection;
public class CollectionDemo {
public static void main(String[] args) {
Collection collection = new ArrayList();
collection.add(1); // 看起来是个1,实际上是个包装类型,因为add方法要求的是Object类型
collection.add(true);
collection.add(1.23);
collection.add("abc");
System.out.println(collection);
//添加一个元素到指定位置
((ArrayList)collection).add(0,"mashibing");
System.out.println(collection);
Collection collection1 = new ArrayList();
collection1.add("a");
collection1.add("b");
collection1.add("c");
collection1.add("c");
//往集合里面加入另一个集合里面的内容
collection.addAll(collection1);
System.out.println(collection);
// collection.removeAll(collection1);
// System.out.println(collection);
System.out.println(collection1.retainAll(collection));
Object[] objects = collection.toArray();
System.out.println(objects[1]);
}
}
三、Collection接口
1、List 接口
特点:
1、可以存放不同类型的数据,二数组只能存放固定类型的数据
2、使用Arraylist子类实现的时候,初始化的长度是10,当长度不够的时候会进行自动扩容操作
Arraylist 示例
package com.msbline.basic.collectionPkg;
import java.util.ArrayList;
import java.util.List;
public class ListDemo {
public static void main(String[] args) {
List list = new ArrayList();
list.add("a");
list.add(1);
list.add(true);
list.add("a");
System.out.println(list);
System.out.println(list.get(0));
System.out.println(list.indexOf("a"));
System.out.println(list.lastIndexOf("a"));
System.out.println("---------------");
list.set(0,"b");
System.out.println(list);
System.out.println("----------------");
List list1 = list.subList(0,2);
System.out.println(list1);
}
}
LinkedList 示例
package com.msbline.basic.collectionPkg;
import java.util.LinkedList;
public class LinkedListDemo {
public static void main(String[] args) {
LinkedList linkedList = new LinkedList();
linkedList.add(123);
linkedList.add(false);
linkedList.add("abc");
System.out.println(linkedList);
linkedList.add(0,"mashibing");
System.out.println(linkedList);
linkedList.addFirst("a");
System.out.println(linkedList);
linkedList.addLast("b");
System.out.println(linkedList);
System.out.println("element:"+linkedList.element());
linkedList.offer("3333");
System.out.println(linkedList);
System.out.println("peek:"+linkedList.peek());
System.out.println("poll:"+linkedList.poll());
}
}
Vector 示例
1、Vector也是List接口的一个实现
2、Vector跟Arraylist一样,底层都是使用数组进行实现的
3、跟Arraylist的区别是:
(1)、Arraylist是线程不安全的,效率高,Vector是线程安全的效率低
(2)、Arraylist扩容的时候,是扩容1.5倍,Vector扩容的时候,是扩容2倍
package com.msbline.basic.collectionPkg;
import java.util.Vector;
public class VectorDemo {
public static void main(String[] args) {
Vector vector = new Vector();
vector.add(1);
vector.add("abc");
System.out.println(vector);
}
}
小结
2、Set接口
TreeSet
1、底层是TreeMap,而TreeMap的底层又是红黑树实现的,根据树的特性,TreeSet只能存储相同类型的数据
2、红黑树也符合二叉树(也叫二叉平衡树)的特性,左边节点的值小于双亲节点,右边节点的值大于双亲节点
3、红黑树左右子树的深度差规则是最长路径不超过最短路径的2倍,避免树倾斜太大,保证查找的速度
4、树中的元素是要默认进行排序操作的,如果是基本数据类型,自动比较,如果是引用类型的话该怎么比较呢,需要自定义比较器,往TreeSet里面放的时候它才知道该根据什么进行比较和排序,也就是后面说的Comparable
package com.msbline.basic.collectionPkg;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
public class SetDemo {
public static void main(String[] args) {
//底层是红黑树实现的
TreeSet treeSet = new TreeSet();
treeSet.add(65);
treeSet.add(1);
treeSet.add(3);
System.out.println(treeSet);
TreeSet treeSet = new TreeSet();
treeSet.add(new Person("a",20));
treeSet.add(new Person("b",60));
treeSet.add(new Person("c",80));
treeSet.add(new Person("d",14));
System.out.println(treeSet);
}
}
package com.msbline.basic.collectionPkg;
public class Person implements Comparable{
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public int compareTo(Object o) {
Person p = (Person) o;
if(p.age > this.age){
return 1;
} else if(p.age < this.age){
return -1;
} else {
return 0;
}
}
@Override
public String toString() {
return "Person{" +
"name='" + name + ''' +
", age=" + age +
'}';
}
}
HashSet
1、HashSet使用的是HashMap,而HashMap使用的是数组加链表来存储的,所以HashSet最终使用的也是数组加链表来存储数据的
2、如上图,如果有两个相同的对象往HashSet里面放的时候,如果没有重写对象的equals和HashCode方法,那么比较的是两个对象的地址,两个对象的地址不同,即使内容相同也能同时存在与HashSet中,解决方法是重写equals和HashCode方法
3、小结
package com.msbline.basic.collectionPkg;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class SetDemo {
public static void main(String[] args) {
Set set = new HashSet();
set.add("123");
set.add("123");
set.add(1);
set.add(true);
System.out.println(set);
System.out.println(set.isEmpty());
Iterator iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
//将while循环改成for循环
for(Iterator iter = set.iterator();iter.hasNext();){
System.out.println(iter.next());
}
}
}
四、Map 接口
Map.entry:表示的是K-V组合的一组映射关系,key和value成组出现
api示例
package com.msbline.basic.map;
import java.util.*;
public class MapDemo {
public static void main(String[] args) {
Map<String,Integer> map = new HashMap<>();
map.put("a",1);
map.put("b",2);
map.put("c",3);
map.put("d",4);
System.out.println(map);
System.out.println("isEmpty:"+map.isEmpty());
System.out.println("size:"+map.size());
System.out.println(map.containsKey("a"));
System.out.println(map.containsValue(2));
System.out.println("get:"+map.get("c"));
//遍历操作
Set<String> keys = map.keySet();
for(String key : keys){
System.out.println(key+":"+map.get(key));
}
//这种方式只能获取value值,不能根据value来获取key
Collection<Integer> values = map.values();
for (Integer value : values){
System.out.println(value);
}
//迭代器
System.out.println("------------");
Set<String> keys1 = map.keySet();
Iterator<String> iterator = keys1.iterator();
for (Iterator<String> it = iterator; it.hasNext(); ) {
String key = it.next();
System.out.println(key+":"+map.get(key));
}
System.out.println("Map.entry---------------");
//Map.entry
Set<Map.Entry<String, Integer>> entrySet = map.entrySet();
Iterator<Map.Entry<String, Integer>> iterator1 = entrySet.iterator();
while (iterator1.hasNext()){
Map.Entry<String, Integer> next = iterator1.next();
System.out.println(next.getKey()+":"+next.getValue());
}
}
}
HashMap、HashTable的区别
1、hashmap:数组+链表(1.7),数组+链表+红黑树(1.8), hashtable:数组+链表
2、HashMap线程不安全,效率比较高,hashtable线程安全,效率低
3、hashmap中的key和value都可以为空,hashtable里面都不可以为空
4、HashMap继承于AbstractMap类,hashTable继承与Dictionary类
5、迭代器(Iterator)。HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException
6、容量的初始值和增加方式都不一样:HashMap默认的容量大小是16;增加容量时,每次将容量变为"原始容量x2"。Hashtable默认的容量大小是11;增加容量时,每次将容量变为"原始容量x2 + 1"
7、添加key-value时的hash值算法不同:HashMap添加元素时,是使用自定义的哈希算法。Hashtable没有自定义哈希算法,而直接采用的key的hashCode()
HashMap
1、hashmap:数组+链表(1.7),数组+链表+红黑树(1.8)
2、默认初始容量16(1<<4),可以自己传入,只不过你传入的容量如果不是2的n次幂,会通过一个计算给你转成离你这个数据最近的2的n次幂数,比如13经过计算得到16,17经过计算得到32,最大容量1<<30(2的30次方),为什么要设为2的n次幂,是为了方便进行&运算提高效率,在计算放在数组的哪个位置的时候采用&运算而没有采用取模运算
3、默认加载因子0.75f,这个是用来决定什么时候进行扩容的,比如当前容量是32,那么32*0.75=24;当数组容量达到24的时候就扩容,这个加载因子也可以自定义
4、当链表长度达到8的时候,转为红黑树,为什么(泊松分布),也是为了提高搜索效率,如果一直使用链表那么在遍历的时候要遍历n次,时间复杂度是O(n),而遍历红黑树的时间复杂度是O(logn)
5、hashmap既然是什么时候创建数组的呢,new HashMap的时候没有创建,因为这个时候还不知道这个map到底会不会放数据呢,如果这个时候就创建了数组最后不放数据就造成了空间浪费,所以是在put的时候才会判断,如果数组还没有创建,那么就这个时候创建
6、扩容的时候创建了一个新的数组,将原数组中的值重新进行hash计算,再放到新的数组中
7、jdk1.8的变化:7.1、jdk1.7的时候,链表节点类的名称叫做Entry,1.8改为Node,不过里面的内容是没有变化的
7.2、1.8里面多了一个TreeNode,是红黑树节点的类
7.3、1.8里面的哈希计算有变化,有一个名词叫做扰动函数(让高位参与运算,减少hash碰撞的概率),现在还不太理解,先记下
HashTable
1、默认初始容量11
LinkedHashMap
参考这篇文章:https://segmentfault.com/a/1190000012964859
TreeMap
参考:https://segmentfault.com/a/1190000012775806
五、Iterable接口、Iterator接口
在Java代码中包含三种循环方式,do...while,while,for
还有一种增强for循环的方式,可以简化循环的编写
所有集合类都默认实现了Iterable的接口,实现此接口意味着具备了增强for循环的能力,也就是for-each
Iterable的接口:
(1) iterator() 返回一个Iterator的具体实现
(2) foreach() 增强for循环
Iterator的接口:
(1) hasNext() 是否还有下一个元素
(2) next() 获取下一个元素
注意:
(1) 在使用iterator的过程中,如果删除(list.remove(o))其中的某个元素会报错,因为list和iterator是两个不同的对象,两个对象操作同一份数据,一个在遍历一个在删除,导致并发操作异常,因此,如果遍历的同时需要修改元素,就使用iterator来操作,由这一个对象对数据进行修改,便不会有并发操作这种异常
(2) ListIterator不仅可以向后遍历,还可以向前遍历,不过得先向后再向前,始终是通过cursor和lastret的指针来获取元素值及向下的遍历索引
package com.msbline.basic.collectionPkg;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.ListIterator;
public class IteratorDemo {
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
// for(int i = 0; i<list.size(); i++){
// System.out.println(list.get(i));
// }
//迭代器
ListIterator iterator = list.listIterator();
while (iterator.hasNext()){
Object o = iterator.next();
if (o.equals(2)){
iterator.remove();
}
System.out.println(o);
}
System.out.println("--------------");
while (iterator.hasPrevious()){
System.out.println(iterator.previous());
}
System.out.println("--------------");
// //增强for循环
// for (Object o : list){
// System.out.println(o);
// }
}
}
六、泛型
当做一些集合的统一操作的时候,需要保证集合里面的数据类型是一致的,此时就需要泛型来进行限制
优点:
1、数据安全,只能放泛型规定的类型
2、获取数据时效率比较高
泛型的高阶应用
1、泛型类
在定义类的时候,在类名的后面加,起到占位的作用,类中的方法返回值类型和属性的类型都可以使用示例
package com.msbline.basic.collectionPkg;
public class FaxingClass<A> {
private String name;
private A a;
public FaxingClass() {
}
public FaxingClass(String name, A a) {
this.name = name;
this.a = a;
}
public void show(){
System.out.println("name:"+name+", id:"+a);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public A getA() {
return a;
}
public void setA(A a) {
this.a = a;
}
}
package com.msbline.basic.collectionPkg;
import java.util.ArrayList;
import java.util.List;
public class FanXingDemo {
public static void main(String[] args) {
FaxingClass<String> fxc = new FaxingClass<>();
fxc.setName("zhangsan");
fxc.setA("male");
fxc.show();
FaxingClass<Integer> fxc1 = new FaxingClass<>();
fxc1.setName("zhangsan");
fxc1.setA(1);
fxc1.show();
FaxingClass<Person> fxc3 = new FaxingClass<>();
fxc3.setName("zhangsan");
fxc3.setA(new Person("lisi",1));
fxc3.show();
}
}
2、泛型接口
示例
package com.msbline.basic.collectionPkg;
public interface FanxingInterface<B> {
public B test();
public void test2(B b);
}
package com.msbline.basic.collectionPkg;
public class FanXingInterfaceImpl implements FanxingInterface<String>{
@Override
public String test() {
return "abc";
}
@Override
public void test2(String s) {
System.out.println(s);
}
}
FanXingInterfaceImpl fx1 = new FanXingInterfaceImpl();
System.out.println(fx1.test());
fx1.test2("1");
3、泛型方法
在定义方法的时候,指定方法的返回值和参数是自定义的占位符,可以是类名中的T,也可以是自定义的Q,只不过在使用Q的时候要定义在返回值的前面示例
package com.msbline.basic.collectionPkg;
public class FanXingMethod<T> {
private T t;
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
public T test() {
// return t;
return (T) "aaa";
}
public <Q> Q show(Q q){
System.out.println(q);
System.out.println(t);
return q;
}
}
FanXingMethod fxm = new FanXingMethod();
fxm.setT("ttt");
fxm.show(123);
fxm.show(true);
System.out.println(fxm.test());
4、泛型的上限(还不太理解)
如果父类确定了,所有子类都可以之间使用5、泛型的下限(还不太理解)
如果子类确定了,子类的所有父类都可以直接传递参数值
普通泛型示例
package com.msbline.basic.collectionPkg;
import java.util.ArrayList;
import java.util.List;
public class FanXingDemo {
public static void main(String[] args) {
//加了泛型之后就只能放Person类型的对象
List<Person> list = new ArrayList();
list.add(new Person("zhangsan",10));
for(int i = 0; i<list.size(); i++){
System.out.println(list.get(i));
}
for(Object iter:list){
System.out.println(iter);
}
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。