容器的概念
如果并不知道程序运行时会需要多少对象,或者需要更复杂的方式存储对象,可以使用Java集合框架
Java集合框架包含的内容
容器API
Collection接口
Collection接口常用方法
- Collection子类可以存储不同类型的数据
- 默认长度是10,容量不够时将自动扩容1.5倍
public class CollectionDemo{
public static void main(String[] args){
Collection collection = new ArraryList();
collection.add(1);//add必须传入Object对象,因此1自动装箱成包装类
collection.add(true);//同上
collection.add(1.23);
collection.add("abc");
System.out.println(collection);
((ArrayList)collection).add(1,"some");//强制转换在第1个位置添加“some”
System.out.println(collection);
Collection collection1 = new ArrayList();
collection1.add("a");
collection1.addAll(conllection);//添加另外一个集合
collection1.clear();//清空集合中的元素
collection.contains("abc");//判断集合中是否包含指定元素
collection.contaninsAll(collection1);//判断是否包括另外一个集合
collection.isEmpty();//判断是否为空
collection.remove("abc");//删除指定元素
collection.retainAll(collection1);//判断一个集合是否拥有另外一个集合的所有元素
collection.size();//返回集合大小
Object[] object = collection.toArray();//collection.toArray().var+回车自动补全类型,toArray()将集合转换为数组返回
}
}
List接口
- List接口存储一组元素不唯一但有序(插入顺序有先后)的对象
- List继承了Collection所以大部分方法重写于Collection
ArraryList
ArratyList实现了长度可变的数组,在内存中分配连续的空间
优点:遍历和查询速度快
缺点:添加删除速度慢
List list = new ArrayList();
list.add("a");
list.add(1);
list.add("a");
list.add(true)
list.indexOf("a")//从头到位找一个“a”返回所在位置下标
list.lastIndexOf("a")//从后开始找
list.set(0,"haha")//指定位置插入元素
List list2 = list.subList(0,2);//截取0~2的元素返回新List
List list3 = List.of(1,2,3,4);//创建新List的一种方法,jdk12后添加
LinkedList
- LinkedList采用链表的存储方式
- 优点:插入删除效率高
缺点:查询效率低
LinkedList linkedList = new LinkedList();
linkedList.add(123);//尾部插入后返回true
linkedList.add(0,"abc");
linkedList.addFirst(false);//头部插入
linkedList.addLast("hhh");//尾部插入无返回值
linkedList.element();//获取第一个元素
linkedList.size();//获取大小
Vector
Vector用法大致同ArrayList
- 也是List接口的一个子类实现
- 与ArrayList一样是数组
- ArrayList线程不安全,效率高。Vector线程安全,效率低
- ArrayList进行扩容是原来大小乘1.5倍,Vector扩容是原来的两倍
Iterator接口
1.迭代器接口,多用于增强for循环
2.在所有的集合类中都默认实现了Iterable接口,因此可以用foreach进行遍历
3.在iterator的方法中,要求返回一个Iterator的接口子类实例对象
此接口包含:hasNext()、next()等方法
4.增强for实际上使用了iterator接口
5.对集合的元素进行增加或删除操作时必须用对应集合下的Iterator内部类实例化对象进行操作
Iterator
ArrayList list = new ArraryList();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
//迭代器
Iterator iterator = list.iterator();//返回集合内实现了Iterator接口的内部类实例化对象
while(iterator.hasNext()){
System.out.println(iterator.next());
}
for(Object i:list){//foreach
System.out.println(i);
}
ListIterator
ArrayList list = new ArraryList();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
ListIterator iter = list.listIterator();
while(iter.hasNext()){
Object o = iterator.next();
if(o.equals(1)){
iter.remove();//使用对应类的内部类迭代器实例化对象进行元素删除
}
System.out.println(o);
}
//--------------
//向前遍历
while(iterator.hasPrivious()){
System.out.println(iterator.previous());
}
list.contains()
List list = new ArrayList();
Person p1 = new Person("zhangsan",13)//假设有个Person类,该类有两个成员变量
Person p2 = new Person("lisi",11)
Person p3 = new Person("lisi",16)
list.add(p1);
list.add(p2);
//没重写对象的equals()和hashCode()方法前
System.out.print(list.contains(p3));//false比较地址值
//重写对象的equals()和hashCode()方法后
System.out.print(list.contains(p3));//true比较对象的hash值
Set接口
1.存储一组元素唯一,但无序的对象,操作数据的方法与List类似
2.Set不存在get()方法
Set set = HashSet();//同样实例化子类对象
set.add("123");
set.add(1);
set.add(true);
set.add("123")//元素重复,不会重复添加
HashSet
- 用HashMap实现
- 采用Hashtable存储结构
- 优点:添加查询删除速度快
缺点:无序 - LinkedHashSet:采用哈希表存储结构,但同时用链表维护次序,因此有序
可以存储任意类型数据
TreeSet
1.采用二叉树(红黑树)存储结构
2. 优点:有序,查询速度比List快
缺点:速度次于HashSet
3. 必须存储同类型的数据
4. 元素间的比较需要实现Comparable接口
与list的contains()原理相同,Set添加元素时比较是否有相同元素在未重写equals()和hashCode()方法时比较的是元素地址,在重写equals()和hashCode()方法后先比较hash值再用equals()进行比较
Comparable接口
使用需要进行元素比较的数据集合需要实现Comparable接口
TreeSet
- 所有可以排序的类都实现了java.lang.Compatable接口
- 实现了Comparable接口的类通过实现comparaTo方法从而确定该类对象的排序方式
内部比较器:
- compataTo定义在元素的类中,该元素的类实现Comparable接口的方式成为内部比较器
public class Person implements Comparable{
public Person(string name,int age)
{
//...
}
public int comparaTo(Object o){
Person p = (Person) o;
if(//...){
//...
return 1;//this大于o
}else if(//...){
return -1;//this小于0
}else{
return 0;//等于
}
}
}
外部比较器
- 需要继承Comparator接口实现compare方法
- 定义在使用集合的类中,且集合所存储的元素类外(元素对象所属类的外部,其他类中)的比较器称为外部比较器,需要把比较结果传递到集合中
- 外部比较器可以定义成一个工具类,供其他需要进行比较的类使用
- 内部比较器和外部比较器同时存在优先使用外部比较器
public class SetDemo implements Comparator<Person>{//Comparator<Person>为泛型写法,具体参考介绍泛型的文章
TreeSet treeSet = new TreeSet(new SetDemo());
treeSet.add(new Person("lisi",11));
treeSet.add(new Person("libai",20));
public int compare(Person p1,Person p2){
if(//...){
//...
return 1;//this大于o
}else if(//...){
return -1;//this小于0
}else{
return 0;//等于
}
}
}
Map接口
HashMap
- 用数组+链表+红黑树的结构存储
- 特点key-value映射
- 创建HashMap对象时并未分配数组空间,在使用put方法时创建并分配数组空间
- 构造方法可传入初始空间大小,必须大于0,但用put方法往里面插入数据时,初始大小会转为大于构造方法传入数值的最小 2 n 2^n 2n,如11转为 2 4 = 16 2^4=16 24=16,但允许传入大小不超过 2 30 2^{30} 230
- 也可传入扩容因子,默认为0.75,如果已使用空间大于长度的0.75则会进行扩容,扩容为长度*2,因此扩容后大小也为2的幂次
- 第4条会把数组长度限定在 2 n 2^n 2n次方大小,长度减1后的二进制数全为1,取全1的个数为位数,以此作为基础后,HashMap存放元素位置的计算方式为:用key的哈希值高16位与低16位进行位异或后,与前面的位数进行位与(高于 位数 的位会被丢弃) 后转为10进制则为元素散列后存放的位置(此条不一定准确,不同jdk散列方法也不一定一致,上述方法被称为扰动函数)
- 从上面几点可以看出,强制为 2 n 2^n 2n大小仅仅是为了方便进行散列找出元素存放位置,以及快速存取元素和减少hash冲突,且位运算通常比取模运算速度快,扩容时转移旧的table到新table时将按新的table长度来重新计算散列位置
- 位置冲突的元素会以链表或红黑树的数据结构进行存储(jdk1.8前为纯链表,1.8之后为链表长度大于等于7则把链表转换为红黑树,定为7是因为通过泊松分布进行统计后得出来的结论,如果长度小于等于6时时间复杂度相差不多,大于8时相差较大,所以取8),不扩容但冲突时链表插入使用尾插法,扩容转移时链表的先后顺序会倒转过来,是因为转移时链表的插入方式为头插法
HashMap常用方法:
Map<String,Integer> map = new HashMap<String,Integer>();
map.put("a",1);
map.put("b",2);
System.out.print(map);
System.out.print(map.isEmpty());
System.out.print(map.size());
System.out.print(map.containsKey("a"));
System.out.print(map.containsValue(2));
System.out.print(map.get("a"));
map.remove("a");
map.clear();//清空操作
//遍历操作
Set<String> keys = map.keySet();
for(String key:keys){
System.out.print(map.get(key));
}
Collection<Integer> values = map.values();
for(Integer v:values){
System.out.print(v);
}
//迭代器
Set<String> keys2 = map.ketSet();
Iterator<String> iterator = key2.iterator();
while(iterator.hasNext()){
String key = iterator.next();
System.out.print(map.get(key));
}
Map是java中的接口,Map.Entry是Map的一个内部接口。
Map提供了一些常用方法,如keySet()、entrySet()等方法,keySet()方法返回值是Map中key值的集合;entrySet()的返回值也是返回一个Set集合,此集合的类型为Map.Entry。
Map.Entry是Map声明的一个内部接口,此接口为泛型,定义为Entry<K,V>。它表示Map中的一个实体(一个key-value对)。接口中有getKey(),getValue方法。
HashMap的内部Entry类(jdk1.8开始为Node)实现了Map.Entry接口
用Entry进行遍历
//Map.entry
Set<Map.Entry<String,Integer>> entries = map.entrySet();
Iterator<Map.Entry<String,Integer>> iter1 = entries.iterator();
while(iter1.hasNext()){
Map.Entry<String,Integer> next = iterator.next();
System.out.print(next.getKey()+" "+next.getValue());
}
HashMap与HashTable:
Map与Table用法类似
HashMap线程不安全,效率高,key value都可以为null
HashTable线程安全,效率低,key value不允许为null
LinkedHashMap
用链表结构存储
有序的HashMap 速度快
TreeMap
用红黑树结构存储
有序 速度没有HashMap快
Set与Map区别
Set没有key,Map以key作为索引存储value
Collections工具类
Collections提供静态方法,可以对Collection及其子类集合进行排序、查找、替换等操作
List<String> list new ArraryList<String>();
list.add("a");
list.add("bn");
list.add("cvb");
list.add("dfgh");
Collections.addAll(list,"erty","fghj","ghjkl");//addAll可以用于给集合添加元素,addAll为参数个数不定的函数
list.sort(new Comparator<String>(){//匿名内部类自定义外部比较器
@Override
public int compare(String o1,String o2){
if(o1.length()<o2.length()){
return -1;
}else if(o1.length()>o2.length()){
return 1;
}
else{
return 0;
}
}
});
Collections.sort(list);//排序
Collections.sort(list,new Comparator<String>(){//匿名内部类自定义外部比较器
@Override
public int compare(String o1,String o2){
if(o1.length()<o2.length()){
return -1;
}else if(o1.length()>o2.length()){
return 1;
}
else{
return 0;
}
}
});
Collections.binarySearch(list,"bn");//排序后才能使用的二分查找
Collections.fill(list,"haha");//替换所有元素为哈哈
Collections.shuffle();//随机排序
Collections.reverse();//逆序
Arrarys
数组操作工具类,提供集合和数组之间的转换
List<Integer> ints = Arrays.asList(1,2,3,4,5);
Object[] object = ints.toArrays();
集合总结