6.集合
6.1集合概念
Java中对于各种数据结构的实现,就是我们用到的集合。
集合大概相当于c语言中的结构体,可以储存同一个类型的东西,比如你定义了一个类people,他有年龄和身高,性别的属性,经过实例化后,想要输出这谢人,数组就不够用了,集合刚好可以表示这些东西.
数组还有个缺点是必须连续的内存,而且数组被定义后长度不会再变化,我们通常都需要可以变长的容器来表示数据.
6.2集合体系
集合可以分为单列结合和双列集合,上面是单列集合的体系,双列集合又叫map,它可以分为 Treemap,hashmap和hashtable.
单列集合又可以分为值可以重复的List和值不可以重复的Set.
List下面又有 数组列表(Arraylist)和数组链表(Linkedlist) ,vector.
Set下面有Hashset和Treeset
6.3collection接口和list类
collection接口包含List和Set2种,其中List是有序的可重复的集合,Set是无序的不可重复的集合.
Collection接口中有一些2中共有的方法,比如
boolean类型的都是成功返回true,失败false的
boolean add(Object element);//增加元素值,成功返回ture,失败false;
boolean addAll(Collection c);//把一个集合添加到另一个集合中去
boolean remove(Object element);//删除某一个指定元素值
boolean removeAll(Collection c);//删除指定集合 不包含时,删除相交的元素
void clear();//清空集合
int size();//返回集合的长度
boolean isEmpty();//判断集合是否为空
boolean contains(Object element);//判断一个元素是否在集合中
boolean containsAll(Collection c);//判断一个集合是不是在这个集合中
boolean retainAll(Collection c);//求交集,集合数据发生变化返回true,不变返回false
List继承了Collection接口,有三个实现的类
ArrayList数组列表,数据采用数组方式存储。
LinkedList链表
Vector数组列表,添加同步锁,线程安全的.
6.3.1ArrayList
ArrayList实现了长度可变的数组,在内存中分配连续的空间。遍历元素和随机访问元素的效率比较高,但是他增删元素的效率很低,
删除是同理的操作,他需要移动的元素很多,增删多的时候就不建议用Arraylsit了.
Arraylist 常用的一些方法:
add(int index, E element)//指定位置曾加指定值
get(int index)//获取某个下表的元素的值
indexOf(Object o)//获取某个值的下表索引
lastIndexOf(Object o)//获取最后一个出现的某个值的下标索引
remove(int index)// 删除并返回指定位置元素
removeRange(int fromIndex, int toIndex) //删除指定区间的元素(子类继承使用)
set(int index, E elemen)//用指定的元素替换此列表中指定位置的元素
实例:
public class a {
public static void main(String[] args) {
Collection<String> a=new <String>ArrayList();
Collection <String> b=new <String>ArrayList();
b.add("c");
b.add("d");
b.add("b");
a.add("a");//向集合添加元素
a.add("b");//可以有重复元素
// System.out.println( a.removeAll(b));//移除所有a里面包含b的东西,数组变化了为true,没有变化为false;
System.out.println(a);
// System.out.println(a.containsAll(b));
//System.out.println(a.addAll(b));//吧b添加到a里
//System.out.println(a.contains("b"));//判断集合是否包含这个元素
//a.clear();//清空集合
//System.out.println(a.remove("b"));//删除这个元素,删除成功为true,失败为false;
//System.out.println(a.isEmpty());//集合是否为空
//System.out.println(a.size());//集合长度
// System.out.println(a);//输出集合
}
}
arraylist中add()的工作原理
他会检测数组够用吗,如果不够用会增大1.5倍的容量然后继续操作
ArrayList扩容机制,在第一次使用Add方法的时候,系统会进行一次扩容,容量为10,当我们添加元素超过10的时候,他会再进行扩容,扩容量为上一次容量的1.5倍,第二次为15,第三次为22等.
6.3.2LinkedList
LinkedList采用链表存储方式。插入、删除元素时效率比较高.
他的增删只需要找下标即可,但他的查找很慢,需要从第一个或者最后一个开始一个一个找.
add(int index,Object element)//向指定索引位置添加指定的值
addFirist(Object element)//往头节点增加值
addLast(Object element)//往尾节点增加值
get(int index)//获取某个索引位置的数据的值
removeFirst()//删除第一个节点
removeLast()//删除最后一个节点
remove(int index)//删除某个索引及其值
getFirst()//获取第一个节点的值
getLast()//获取最后一个节点的值
实现方法和上面的ArrayList差不多就不写了
6.3.3vector
vector 是添加了同步锁的一个顺序数组容器.
add(E e) //将指定的元素追加到此Vector的末尾。
add(int index, E element) //在此Vector中的指定位置插入指定的元素。
capacity() //返回此向量的当前容量。
clear() //从此Vector中删除所有元素。
contains(Object o) //如果此向量包含指定的元素,则返回 true 。
elementAt(int index) //返回指定索引处的组件。
equals(Object o) //将指定的对象与此向量进行比较以获得相等性。
equals(Object o) //将指定的对象与此向量进行比较以获得相等性。
indexOf(Object o) //返回此向量中指定元素的第一次出现的索引,如果此向量不包含元素,则返回-1。
indexOf(Object o, int index) //返回此向量中指定元素的第一次出现的索引,从 index向前 index ,如果未找到该元素,则返回-1。
set(int index, E element) //用指定的元素替换此Vector中指定位置的元素。
toArray() //以正确的顺序返回一个包含此Vector中所有元素的数组。
实现方法和上面的ArrayList差不多就不写了
6.3.4迭代器
迭代器iterator,专业遍历集合
迭代器有3个方法:hasnext()有没有下一个
next()拿到下一个的值,remove()删除
使用方法
public static void main(String[] args) {
ArrayList <String> a=new ArrayList();
a.add("a");
a.add("b");
a.add("c");
a.add("d");
a.add("e");
Iterator it= a.iterator();//返回一个迭代器对象
while(it.hasNext()){//当他有下一个元素为真的时候
String b=(String)it.next();//把下一个元素赋给b
System.out.println(b);//输出b
}
}
6.3.5对单列集合遍历的三种方法
for()循环遍历
for(int i=0;i<a.size();i++){//循环开始
String b=a.get(i);//获取i个位置的值赋给b
System.out.println(b);//输出b
}
增强for遍历
for(String b:a){//结构就是这样
System.out.println(b);
}
迭代器遍历
上面已有就不写了
还有一个逆序输出一个集合的
ListIterator <String>it=a.listIterator(a.size());
while(it.hasPrevious()){
String b=it.previous();//把前驱赋给一个字符串b//
if(b.equals("a")){//
a.remove("a");//
break;//
}
System.out.println(b); }
6.4Set类
Set接口继承了Collection接口。
Set中所存储的元素是不重复的,但是是无序的, Set中的元素是没有索引的
Set接口有两个实现类
HashSet类中的元素不能重复,即彼此调用equals方法比较,都返回false。
底层数据结构是哈希表+链表
哈希表依赖于哈希值存储.
可以给Set集合中的元素进行指定方式的排序。存储的对象必须实现Comparable接口。
TreeSet底层数据结构是二叉树(红黑树是一种自平衡的二叉树.
6.4.1HashSet类
Hashset元素不能重复,在添加元素的时候,怎么判断他是否重复呢,这里会用到2个方法,hashcode()和equals();
在一般的比较方法的时候,都是通过equals()方法进行比较,但是他的效率有点低,比如说String a=“abcdwdadad”,String b=“agagagagg”;如果用equals方法去比较的话,他是一位一位的比较,他的效率就会很低。但是他是安全的方法,而我们可以把他转换位hash值,比较它们的哈希值,但是这个方法又不安全,比如说
Stirng a=”通话“,String b=“重地”,在经过equals方法比较后,他们会返回true,即表明,这2个字符串的哈希值是一样的,而他们其实是不一样的,所以hashcode()方法不太安全。但是他效率高,我们为了更好的比较hashset中的元素是否重复,所以我们把2个方法结合起来。先用hashcode方法转化他们的哈希值,比较他们的哈希值,如果哈希值不相同,则这两个元素也就不重复,如果哈希值相同的时候,我们在调用equals方法去比较他们是不是真的相同,这就是hashset判断是否元素重复的方法。
hashset的底层实现是hashmap。
hashset也有一些常用的方法,比如add()
add(E e) //将指定的元素添加到此集合(如果尚未存在)。
clear() //从此集合中删除所有元素。
contains(Object o)// 如果此集合包含指定的元素,则返回 true 。
contains(Object o) //如果此集合包含指定的元素,则返回 true 。
remove(Object o) //如果存在,则从该集合中删除指定的元素。
size() //返回此集合中的元素数(其基数)。
实现一个hashset的实例
public class person {
private int age;
private String name;
@Override
public String toString() {
return "person{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
person person = (person) o;
return age == person.age &&
Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(age, name);
}
public person(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
可以写一个person类,把person类装入一个hashset中,必须要重写hashcode和equals方法和一些构造方法等
public static void main(String[] args) {
HashSet<person>a=new HashSet<>();
person b=new person(1,"sb");
person b1=new person(2,"sd");
person b2=new person(3,"sa");
person b3=new person(4,"sc");
person b4=new person(1,"sb");
a.add(b);
a.add(b1);
a.add(b2);
a.add(b3);
a.add(b4);
System.out.println(a);
}
他不能有重复的值所有不会有5个值
[person{age=2, name='sd'}, person{age=3, name='sa'}, person{age=4, name='sc'}, person{age=1, name='sb'}]
6.4.2Treeset
可以给Set集合中的元素进行指定方式的排序。存储的对象必须实现Comparable接口。
TreeSet底层数据结构是二叉树(红黑树是一种自平衡的二叉树)
Treeset实现的排序需要重写comparable方法例如下面代码,如果需要得到某种有顺序的结果可以选用treeset
public class Car implements Comparable<Car>{
private int id;//car类可以充当泛型,他有属性id和name
private String name;
public int getId() {//生成一下他的一些方法和构造函数和comparable方法
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Car(int id, String name) {
this.id = id;
this.name = name;
}
public String toString() {//toString是为了方便输出表达
return "treeset{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
@Override
public int compareTo(Car o) {
return this.name.compareTo(o.name);//给name排序
// return this.id-o.id;给id排序
}
}
下面的treeset方法调用car类
public class treesettest {
public static void main(String[] args) {
TreeSet<Car> a=new TreeSet<>();
Car car1=new Car(1,"bm1");
Car car2=new Car(2,"bm2");
Car car3=new Car(3,"bm3");
Car car4=new Car(4,"bm4");
Car car5=new Car(1,"bm5");
a.add(car1);
a.add(car2);
a.add(car3);
a.add(car4);
a.add(car5);
System.out.println(a);
}
}
[treeset{id=1, name='bm1'}, treeset{id=2, name='bm2'}, treeset{id=3, name='bm3'}, treeset{id=4, name='bm4'}, treeset{id=1, name='bm5'}]这是那name排序的结果
[treeset{id=1, name='bm1'}, treeset{id=2, name='bm2'}, treeset{id=3, name='bm3'}, treeset{id=4, name='bm4'}]这是那id排序的结果
6.4.3set接口的一些遍历方法
增强for
迭代器
和list中的增强for和迭代器使用方法基本相同的
6.5Map接口
map类是双列结构,是由键-值对组成的一系列结构。
将键映射到值的对象,一个映射不能包含重复的键,每个键最多只能映射到一个值
Map接口常用的方法
V put(K key,V value)//增加一对键-值对 key是键,value是值
V remove(Object key)//删除键,也就删除了值了
void clear()//清空所有
boolean containsKey(Object key)//判断是否包含这个键
boolean containsValue(Object value)//判断是否包含这个值
boolean isEmpty()//判断是否位空
int size()//返回map的长度
V get(Object key)//得到某个键的值
Collection<V> values()//把所有的值返回一个collection集合
Set<K> keySet()//把所有的键返回一个set集合
Set<Map.Entry<K,V>> entrySet(//把键值对放进一个entry类型的set集合
3种方法实现了Map接口
(1)HashMap
HashMap中元素的key值不能重复, 排列顺序是不固定的,可以存储一个为null的键。
(2)TreeMap
TreeMap中所有的元素都保持着某种固定的顺序,如果需要得到一个有序的Map就应该使用TreeMap,key值所在类必须实现Comparable接口。
(3) HashTable
实现了同步。不能存储为null的键
6.5.1Hashmap
Hashmap是集hashset ,链表,红黑树于一身的数据结构.
为什么他的查找速度很快呢,因为
哈希冲突:如果两个不同的元素,通过哈希函数得出的实际存储地址相同怎么办?也就是说,当我们对某个元素进行哈希运算,得到一个存储地址,然后要进行插入的时候,发现已经被其他元素占用了,其实这就是所谓的哈希冲突,也叫哈希碰撞。而HashMap即是采用了链地址法,也就是数组+链表的方式。
下面是hashmap底层方法的一些理解
这里的(1)表示hashtable结构,他也是个数组结构,它默认16个,当你存入一个98的时候,对98mod16得到的结果为2,所以把他存入2下面的链表里,加入还要存入一个34,对他mod一下,余数为2,再把它存入2下面的链表种,当好多余数为2的数据是,下面的链表就会越来越差,这也导致了很多数据操作很麻烦,比如遍历的话。所以当链表的数据超过7个的时候 我们会自动把他转化成一个红黑树,他是自平衡的,所以遍历起来就会方便一点.
这就是比较简略的原理了.
他还有最大容量 2^30,负载因子0.75,当哈希表容量到达整数数组的0.75时,进行扩容,扩容为原来的2倍.
下面是一个图
hashmap还有一些其他的常用方法:
entrySet() //返回此地图中包含的映射的Set视图。
get(Object key)//返回到指定键所映射的值,或 null如果此映射包含该键的映射。
keySet() //返回此地图中包含的键的Set视图。
put(K key, V value)// 将指定的值与此映射中的指定键相关联。
remove(Object key) //从该地图中删除指定键的映射(如果存在)。
remove(Object key) //从该地图中删除指定键的映射(如果存在)。
remove(Object key) //从该地图中删除指定键的映射(如果存在)。
6.5.2Treemap
Treemap是一个能比较元素大小的map集合,会对传入的key值进行大小排序,其中的排序可以是元素的自然排序还可以是自定义排序,比如你定义了一个Treemap他的键为id,值为一串字母,它也可以id排序,也可以对字母排序的.如果需要得到一个有序的map就该使用treemap,其中,key值必须实现comparable接口。
Treemap是实现了红黑树结构的,是一颗二叉树。
下面是对他排序的一些实现
public class sort implements Comparable<sort>{
private int age;
public sort(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "sort{" +
"age=" + age +
'}';
}
@Override
public int compareTo(sort o) {
return this.age-o.age;
}
}
public class Treemap {
public static void main(String[] args) {
TreeMap<sort,String> a=new TreeMap();//同样泛型里可以传泛型类
a.put(new sort(1),"a");
a.put(new sort(9),"u");
a.put(new sort(4),"n");
a.put(new sort(7),"c");
a.put(new sort(6),"v");
System.out.println(a.toString());
}
}
{sort{age=1}=a, sort{age=4}=n, sort{age=6}=v, sort{age=7}=c, sort{age=9}=u}
基本和上面的Treeset实现方法类似不过这里的是双列集合而已.sort里面也可以定义多个属性,给多个属性都可以排序.
6.5.3Hashtable
Hashtable也叫哈希表,是Hashmap的一部分,他的数据结构是一个数组.
6.6.Collections类
Collections类是可以同时对list和set结构同时操作的一个类,就和数组中的Arrays类一样。
它有一些常用的方法
addAll(Collection<? super T> c, T... elements);//把后面的元素都可以加到前面的类里
binarySearch(List<? extends Comparable<? super T>> l ist, T key)//求第几个索引的第几个数
sort(List<T> l ist)//排序
sort(List<T> l ist, Comparator<? super T> c)//自定义排序
swap(List<?> l ist, int i, int j)//交换第i个第j个的值
copy(List<? super T> dest, List<? extends T> src) //把后面的集合里的数据复制到前面集合里去,会覆盖前面集合里面的数据; 注意 dest size需大于等于src.size
emptyList()//返回为空的集合,不能添加数据,只是为了避免空指针异常
fill(List<? super T> list, T obj)//那后面的数把前面集合里面的所有数据替代掉
max(Col lection<? extends T> col l)//集合中的最大值
min(Col lection<? extends T> col l)//集合中的最小值
replaceAll(List<T> list, T oldVal, T newVal)//旧值替换新值
reverse(List<?> list)//逆序排序
Collections类的一些实现.
public static void main(String[] args) {
List<Integer> a=new ArrayList<>();//必须是list类和set类的单列集合才能操作。
Collections.addAll(a,1,3,5,2,4);
List<Integer> b=new ArrayList<>();
Collections.addAll(a,5,6,7,8,9);
// Collections.sort(a);
// System.out.println(a);
// Integer b=Collections.binarySearch(a,2);
// Collections.swap(a,2,3);
// Collections.copy(a,b);
// List c=Collections.emptyList();
// Collections.fill(a,5);
// Collections.max(a);
// System.out.println(Collections.max(a));
// System.out.println(Collections.min(a));
// Collections.replaceAll(a,3,2);
Collections.reverse(a);
System.out.println(a);
}
addAll(a,5,6,7,8,9);
// Collections.sort(a);
// System.out.println(a);
// Integer b=Collections.binarySearch(a,2);
// Collections.swap(a,2,3);
// Collections.copy(a,b);
// List c=Collections.emptyList();
// Collections.fill(a,5);
// Collections.max(a);
// System.out.println(Collections.max(a));
// System.out.println(Collections.min(a));
// Collections.replaceAll(a,3,2);
Collections.reverse(a);
System.out.println(a);
}