Java的集合是一个非常庞大的体系,在初学Java时很难弄清楚具体的结构,这里对它的体系从几条不同的路径进行一下梳理,其中有较多不常用的细碎分支没有加入,希望最后能有一个简单清晰的框架结构,Java的集合框架主要包括两种类型的容器,即collection集合和map键值对。先从collection集合开始:
上图是对collection集合结构的框架简单分类,可以看出它主要分为List ,Set,Queue三个接口,具体他们的API.
从API体系图中可以清晰的看到集合的体系,在我们向集合中添加元素时注意他有一个操作,基本数据类型的自动装箱。
public class TextCase01 {
public static void main(String[] args) {
Collection c=new ArrayList();
c.add(1);//自动装箱
这里c.add的参数类型为Object,而输入的1为基本数据类型的int值,这里自动转换为Integer的包装类型,即为自动装箱。
我们将类加入一个容器中时,经常会设置自己的比较逻辑,例如:
public class TextCase02 {
public static void main(String[] args) {
ArrayList stus=new ArrayList();
Stu s1=new Stu("Tom", 1);
Stu s2=new Stu("jack",2);
stus.add(s1);
stus.add(s2);
Stu s3=new Stu("Tom",1);
System.out.println(stus.contains(s3));
}
}
这里直接比较时是无论如何都不会相等的,因为他比较的是地址,不可能相等,所以我们需要加入自己的比较逻辑就是重写equals方法,我们区分什么时候重写方法在我的理解里有三点,一般来说,如果你要吧一个类放入一个容器,一般需要重写他的equals方法用以确认存的是值而不是不能用来比较的地址,如果存入的容器是队列,还需要重写他的hashcode方法,要是存入有序的容器,还需要重写compare to方法。假如不重写equals方法会造成contains,indexof等方法的失灵。
下面为重写equals方法来定义它的比较逻辑:
@Override
public boolean equals(Object o){
Stu s1=this;
Stu s2=(Stu)o;
if(s1.id==s2.id)return true;
else return false;
}
在上面的比较中也可以看出,在集合中,基本数据类型和包装类传递的是值,引用类型传递的是地址。
在集合容器中为什么一般不使用for循环来遍历数组:
List list=new ArrayList();
for(int i=0;i<10;i++){
list.add();//创建一个大小为10的容器;
}
System.out.println(list);
for(int a=0;a<list.size();i++){
list.remove;//逐个删除list中的元素;
}
System.out.println(list);
执行结果我们会发现结果为[1,3,5,7,9],就是进行了隔位删除,产生的原因就是元素个数每次循环减一造成的,要解决这个问题我们可以采取逆序删除的方法,也就是倒着删除,但无论如何这都不是优秀的解决办法,也难免会造成错误。这里同样不可以使用foreach循环解决,因为系统会报异常,正确的循环方法是使用迭代器。下面使用迭代器:
Iterator it= list.Iterator();
while(list.hasnext()){
Integer e=(Integer)it.next();
System.out.println(e)
}
使用迭代器可以正确的打印数组元素。使用it.remove()也可以正确的清空数组。
应该注意的一点是在list列表下提供了一个可供双向循环的迭代器,list.listIterator();这里我们将上面的列表数组再进行反向的输出:
ListIterator it= list.ListIterator();
while(it.hasPrevious()){
Integer e=(Integer)it.previous();
System.out.println(e);
}
这样可以正确的输出数据,也是推荐的方法。
ArrayList和LinkedList的能效问题:
首先,ArrayList的默认的Capacity大小为10。因为ArrayList的底层是数组,所以要想提高能效,可以直接在容量大小上赋初值,这样可以减少在扩容上的能耗。ArrayList的优势是它底层数组,所以乱序访问速度快,LinkedList的优势是双端链表,动态追加效率高。
这里做一个for循环验证一下:
public static void main(String[] args) {
int size=200000;
LinkedList link=new LinkedList();
ArrayList array=new ArrayList();
long start=System.currentTimeMillis();
for (int i = 0; i <size ; i++) {
link.add(0,new Object());
}
long end=System.currentTimeMillis();
System.out.println(end-start);
start=System.currentTimeMillis();
for (int i = 0; i <size ; i++) {
array.add(0,new Object());
}
end=System.currentTimeMillis();
System.out.println(end-start);
}
若将上面的循环输入改为随机数Random,将随机数加入数组随机访问,则会发现ArrayList速度会快非常的多,得出的结果可以看出LinkedList速度明显优于ArrayList,因为后者要进行移位的操作。可以得出一个结论,ArrayList乱序访问快,LinkedList乱序删除快。
接下来是集合中Set接口,Set是一个可迭代的无序不可重复的接口,他的常用实现类就是HashSet(底层实现为map的key,个人理解为key所以不可重复,以后假如有新的认知会来填坑。。),通常我们可以利用他不可重复的特点进行列表去重功能,比循环实现容易的多:
Set set=new HashSet(list);//例子
然后使用set来输出就可以实现。
HashSet和TreeSet的区别是后者的输出是有序的。
HashSet不支持作比较排序,TreeSet支持(重写Compare To的方法)。
数组转为定量列表(大小不再可变的列表)
我们可以通过数组转换为列表,但转成的列表不再支持.add操作:
String[]str={"a","b","c"};
List<String> strs=Arrays.aslist(str);
Strs.add("d");
System.out.println(strs)
这里会报错,系统报不允许二次操作的异常,所以数组转成的列表不允许二次扩容的操作。
同样列表也可以转换成数组:
list.toArray()–>Object[] ;转换成的是一个Object类的数组。
Map
map是做缓存,和临时数据存储最好的接口。
以上就是集合章节的知识点简单总结,还有不少细碎的内容比如compare逻辑等以后再写。