容器
一 泛型
- 泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
1、泛型方法
- 泛型方法的定义规则
- 在方法返回值之前,多个类型参数()间用逗号隔开。
- 返回值类型可以用泛型声明,方法内,泛型可以当作正常的类来使用。
- 泛型只能代表引用型类型,不能是基本类型。
public class GenericMethodTest
{
public static < E > void printArray( E[] inputArray )
{
for ( E element : inputArray ){
System.out.printf( "%s ", element );
}
System.out.println();
}
public static void main( String args[] )
{
Integer[] intArray = { 1, 2, 3, 4, 5 };
Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };
Character[] charArray = { 'H', 'E', 'L', 'L', 'O' };
System.out.println( "整型数组元素为:" );
printArray( intArray );
System.out.println( "\n双精度型数组元素为:" );
printArray( doubleArray );
System.out.println( "\n字符型数组元素为:" );
printArray( charArray );
}
}
- 有界的类型参数
- 上界 <? extends Fruit> ,表示所有继承Fruit的子类,包含Fruit本身
- 下界 <? super Apple>,表示Apple的所有父类,包含Apple本身
2、 泛型类
- 类名后添加类型参数声明,泛型接口使用基本和泛型类差不多
public class Box<T> {
private T t;
public void add(T t) {
this.t = t;
}
public T get() {
return t;
}
public static void main(String[] args) {
Box<Integer> integerBox = new Box<Integer>();
Box<String> stringBox = new Box<String>();
integerBox.add(new Integer(10));
stringBox.add(new String("菜鸟教程"));
System.out.printf("整型值为 :%d\n\n", integerBox.get());
System.out.printf("字符串为 :%s\n", stringBox.get());
}
}
3、 伪泛型的概念
- 泛型只在源码中存在,在编译后的字节码中就已经替换为原来的原生类型了,并且在相应的地方插入了强制类型转换的代码,Java的泛型实现方法称为类型擦除,基于这种方法实现的泛型称为伪泛型
二、容器(集合)概述
1、Java集合框架的体系结构:
说明
- 所有的集合框架体系都包含:接口、实现类和算法
- collection 接口所包含的方法:
三、list
1、常用方法
两个 list 之间的元素操作
- list.containsAll(list2)-------list是否包含list2中所有元素
- list.addAll(list2)--------将list2中所有元素都添加到list中
- list.removeAll(list2)--------从list中删除同时在list和list2中存在的元素
- list.retainAll(list2)-------取list和list2的交集
2、ArrayList
- 特点:查询效率高,增删效率低,线程不安全
- 原理:ArrayList底层使用Object数组来存储元素数据,超出数组默认长度重新搞一个更大的数组,然后将已有内容复制过去
3、LinkedList
- 特点:查询效率低,增删效率高,线程不安全
- 原理:每个数据节点中都有两个指针,分别指向前一个节点和后一个节点
4、Vector
- 和 arraylist 相比,线程安全,效率低,几乎不用了
5、List应用总结
- 不存在线程安全问题时,并且查找较多用ArrayList(一般使用它)
- 不存在线程安全问题时,增加或删除元素较多用LinkedList
四、queue
- 描述:特殊的线性表,特点是先进先出
- 方法:
- offer()来加入元素 poll()来获取并移出元素
- element()或者peek()方法使用前端而不移出该元素
- 注意:LinkedList类实现了Queue接口,因此我们可以把LinkedList当成Queue来用
public class QueueTest {
public static void main(String[] args) {
Queue<String> queue = new LinkedList<String>();
queue.offer("a");
queue.offer("b");
queue.offer("c");
queue.offer("d");
queue.offer("e");
for(String q : queue){
System.out.println(q);
}
System.out.println("===");
System.out.println("poll="+queue.poll());
for(String q : queue){
System.out.println(q);
}
System.out.println("===");
System.out.println("element="+queue.element());
for(String q : queue){
System.out.println(q);
}
System.out.println("===");
System.out.println("peek="+queue.peek());
for(String q : queue){
System.out.println(q);
}
}
}
四、map
1、概述
- Map就是用来存储 键(key)-值(value) 对 的,键不能重复
- Map 接口的实现类有 HashMap、TreeMap、HashTable、Properties 等
2、map接口方法
3、HashMap和HashTable
- 原理:HashMap采用哈希算法实现,底层采用了哈希表存储数据,键不能重复,如果发生重复,新的会替换旧的
- 特点:HashMap在查找、删除、修改方面都有非常高的效率
- 区别:
- HashMap: 线程不安全,效率高。允许key或value为null
- HashTable: 线程安全,效率低。不允许key或value为null
4、HashMap底层实现详解
数据结构中由数组和链表来实现对数据的存储
- 数组:占用空间连续。 寻址容易,查询速度快。但是,增加和删除效率非常低。
- 链表:占用空间不连续。 寻址困难,查询速度慢。但是,增加和删除效率非常高。
- 哈希表的本质就是 数组+链表,集合二者优点。
HashMap的结构
- Entry[] table 就是HashMap的核心数组结构,称之为 位桶数组;
- 一个Entry对象存储了: key对象 ;value对象 ;下一个节点next ;键的hash值;
存数据过程
- 首先调用key对象的hashcode()方法,获得hashcode;
- 根据hashcode计算出hash值,要求转化后的hash值尽量均匀地分布在[0,数组长度-1]这个区间,减少hash冲突;
- 生成Entry对象,将Entry对象放到table数组中:如果本Entry对象对应的数组索引位置还没有放Entry对象,则直接将Entry对象存储进数组,如果对应索引位置已经有Entry对象,则将已有Entry对象的next指向本Entry对象,形成链表;
- hash算法采用 hashcode与(length-1)的位与运算;链表长度大于8时,链表就转换为红黑树,提高了查找效率;
取数据过程
- 通过 key的hashcode 得到 hash 值,通过这个值找到数组的位置;
- 然后在对应的链表上调用 key的 equals 方法逐一与所有节点的 key比较;
- 返回比较后为 true 的节点对应的 value 值;
- Java中规定,两个内容相同(equals()为true)的对象必须具有相等的hashCode,否则Map取数据的过程存在悖论;
扩容
- HashMap的位桶数组,初始大小为16,大小是可变的,如果位桶数组中的元素达到(0.75*数组 length), 就重新调整数组大小变为原来2倍大小;
- 扩容的本质是定义新的更大的数组,并将旧数组内容挨个拷贝到新数组中,很耗时;
5、TreeMap
二叉树
- 基本二叉树
- 排序二叉树
- 左子树上所有节点的值均小于它的根节点的值
- 右子树上所有节点的值均大于它的根节点的值
- 平衡二叉树
- 在平衡二叉树中任何节点的两个子树的高度最大差别为1
- 平衡二叉树建立在排序二叉树的基础上,通过左旋或者右旋的操作来达到平衡
- 红黑二叉树:自平衡的排序二叉树
TreeMap的底层实现
- TreeMap是红黑二叉树的典型实现;
- entry 里面存储了本身数据、左节点、右节点、父节点、以及节点颜色;
TreeMap的使用
- HashMap效率高于TreeMap,一般用他;
- 在需要排序的Map时才选用TreeMap;
五、set
1、概述
- Set容器特点:无序、不可重复(null 值也只能有一个);
- Set常用的实现类有:HashSet、TreeSet等,一般使用HashSet;
2、HashSet
- 使用:使用时注意无序和不可重复的特点
- 底层实现:HashSet本质就是一个简化版的HashMap,数据存到键对象
3、TreeSet
使用:
- 对应的类需要实现Comparable接口,才能根据compareTo()方法比较对象之间的大小,才能进行内部排序;TreeSet中不能放入null元素;
@Override
public int compareTo(User o) {
if (this.id > o.id) {
return 1;
} else if (this.id < o.id) {
return -1;
} else {
return 0;
}
}
底层实现:
- 一个简化版的TreeMap,通过key来存储Set的元素
六、迭代器
1、迭代器
- 一般情况集合在遍历的过程中是不许删除操作的会报错,但是使用迭代器可以实现
Set<String> set = new HashSet<>();
set.add("haha1");
set.add("haha2");
set.add("haha3");
set.add("haha4");
Iterator<String> ite = set.iterator();
while(ite.hasNext()){
String str = ite.next();
if(str.endsWith("3")){
ite.remove();
}
System.out.print(str+"\t");
}
System.out.println();
for (String str:set) {
System.out.print(str+"\t");
}
常用集合的遍历
- List:普通for,增强for和迭代器
- set:增强for和迭代器
- map:增强for和迭代器(keys,values,keyset,entryset)
2、Collections工具类
- Collections 提供了对Set、List、Map进行排序、填充、查找元素的辅助方法
- void sort(List)---------对List容器内的元素排序,排序的规则是按照升序进行排序
- void shuffle(List)-----------对List容器内的元素进行随机排列
- void reverse(List)-----------对List容器内的元素进行逆续排列
- void fill(List, Object)-----------用一个特定的对象重写整个List容器
- int binarySearch(List, Object)-----------对于顺序的List容器,采用折半查找的方法查找特定对象
- 如下情况可能需要重写 equals/hashCode 方法
- 要将我们自定义的对象放入HashSet中处理
- 要将我们自定义的对象作为HashMap的key处理
- 放入Collection容器中的自定义对象后,可能会调用remove、contains等方法时
七、Java数据结构
- Java中的数据结构主要包括以下几种接口和类:
- 枚举(Enumeration)
- 位集合(BitSet):保存位值的特殊数组,有许多位操作的方法
- 向量(Vector):几乎不用这个了
- 栈(Stack)
- 字典(Dictionary):存储键值对,类似于Map,已过时
- 哈希表(Hashtable):几乎不用了
- 属性(Properties)
栈(Stack)
- 特点是后进先出,是Vector的一个子类,常用的方法:
- boolean empty( )
- Object peek( )
- Object pop( )
- Object push(Object element)
- int search(Object element)
属性(Properties)
- HashTable的子类,存键值对,键和值都是String,常用来配合IO流加载配置文件,常用方法如下:
void load(InputStream streamIn) throws IOException
void store(OutputStream streamOut, String description)