java集合

Java中的集合: 
为什么出现集合类? 
面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储,集合就是存储对象最常用的一种方式。 

集合是存储对象的一个工具。 

集合和数组的特点: 
  相同点: 
  数组和集合都是容器。 
   
  不同的: 
集合: 
  1,可以存储对象,只能存储对象 
  2,集合的长度是可变的。 
数组: 
  1,可以存储对象,也可以存储基本数据类型。 
  2,数据长度是固定的。 

**容器对象有很多种,通过内部的数据结构来区分。 
数据结构:就是一种数据存储的方式。 

**容器在不断将共性向上抽取的过程中,就出现了集合体系结构。 
该结构的顶层是一个接口,Collection。 


Collection接口: 
创建一个集合对象,也就是一个容器。 
Collection coll = new ArrayList(); 

1,添加元素 
  Add(); coll.add(“abc”); 
2,删除元素 
  Boolean b = coll.remove(“abc”); 
3,获取集合的长度 
  Int size = coll.size(); 
4,清除集合中的所有元素 
  Coll.clear(); 
5,判断集合是否为空(其实依据的是size()是否等于0判断的) 
  Boolean b = coll.isEmpty(); 
6,添加一堆元素 
  coll.addAll(Collection co1);//添加的是一个集合 
7, 判断一堆元素是否存在 
  coll.containsAll(Collection co1); 
8,删除一堆元素 
  Boolean b = coll.removeAll(Collection co1); 
9,获取两个集合的交集。 
  Boolean b = coll.retainAll(co1); 
  //retainAll会将coll和co1中相同的元素保留在coll中,所以coll中存储的就是交集的元素,当coll集合中的元素变化时,retainAll方法返回的是true,当coll集合中的元素本身就是交集元素,不发生变化,返回false。 
10,Collection集合中对元素的获取方法。 
  Coll.iterator(); 
    
   
**集合容器都具备取出方式,取出方式并不是一个方法。因为一个方法不足以完成取出功能。 
取出是由多个功能来完成,为了方便使用,就将这些功能封装成对象。该对象在描述这些功能会直接使用到具体集合中的数据和集合特点。 
所以这个取出元素的描述类,定义在了集合的内部。也就是一个内部类。 
因为这个类直接访问集合中的数据,根据集合自身的数据结构特点。将具体的方法都定义完成后。需要将对象提供出去。让用户使用。所以暴露了一个方法来完成对这个内部类对象的访问。 
为了提高扩展性,发现只要是容器,就具备取出功能。但是数据结构不同,取出的具体实现就不一样,这时为了扩展,就抽取出了一个接口。Iterator。 

取出对象有两种表现形式: 
1,Iterator it = coll.iterator(); 
While(it.hasNext()){ 
  System.out.println(it.next()); 

2,for(Iterator it = coll.iterator(); it.hasNext(); ){ 
  System.out.println(it.next()); 

第二种可以减少内存的消耗,因为第一种的it使用完之后还存在,而在for循环中,it出了for循环就不存在了 


容器在进行添加的时候add();中的参数是object类型。 
那么在取出的时候就要写成 
Person p1 = (Person)it.next(); 
所以在进行迭代器使用的时候,next方法在循环中,建议只定义一个,定义多个会导致数据错误。当next方法没有获取到元素时,会发生NoSuchElementException; 

**其实集合中存储的都是对象的引用。 
获取迭代器后,迭代器中持有的也是元素的引用。 




Collection 
  |--List:该容器的元素是有序的 (存储的顺序和取出的顺序一致) 
  该集合中的元素都有索引 (角标) ,该集合可以存储重复的元素 
  |--ArrayList:底层数据结构是数组结构 不同步的,查询的速度很快 
  |--vector:底层数据结构是数组结构 jdk1.0版本。 同步的 
  |--LinkedList:底层数据结构是链表结构 不同步的,对元素的增删操作效率很高 
  (vector和ArrayList区别? 
  Vector相对于线程时安全的,同步的。ArrayList是不安全的 
  ) 
  |--Set:无序,不可以存储重复元素。Set接口的方法与collection方法一致。 
  Set没有特殊方法。Set接口取出元素方法只有迭代器。 
  |--HashSet:底层数据结构是哈希表。哈希表这种结构,就是对哈希值的存储。而且 每一个对象都有自己的哈希值。因为object类中有个hashCode方法。 
  (如何保证元素唯一性? 
  通过判断元素的hashCode方法,和equals方法来完成的。当hashCode值相同,会再判断一次equals方法的返回值是否为true。 
  如果hashCode值不同,就确定元素的哈希表中的位置,不同判断equals了。 
  哈希表中有一个桶结构,每一个桶都有一个哈希值,当哈希值相同,但是equals为false时,这些元素都放在同一个桶中。 
  ) 
  |--TreeSet:可以对set集合中的元素进行排序。 
  数据结构是二叉树,这种结构,可以提高排序性能。 
  (如何保证元素唯一性? 
  是根据比较方法的返回值确定的,只要返回的是0,就代表元素重复。 
  ) 
   
   
   
   
   
List:常用的共性方法。(继承了Collection接口) 
1,添加元素。 
  Add(index,element);//在索引位添加元素,其他元素依次顺延。 
2,删除元素。 
  Remove(index);//按照指定索引删除元素。被删除的元素会被返回。 
3,修改元素。 
  Set(index,element);//按照索引修改元素,被修改的元素会被返回。 
4,获取元素。 
  Get(index);//通过索引获取指定元素 
  Get(element);//通过元素获取元素第一次出现的位置 
  subList(fromIndex,lastIndex);//根据头尾角标获取子列表。 
   
  通过List特有的方式,获取集合中所有的元素。(其实就是for循环) 
  For(int x = 0;x < list.size(); x++) 
  { 
  System.out.println(list.get(x)); 
  } 

**在进行迭代过程中,如果出现了迭代器和容器同时对元素进行操作的情况很容易引发 
ConcurrentModificationException并发修改异常。 
所以要么使用集合的方法操作元素,要么使用迭代器的方法操作元素,不要同时使用, 
迭代器Iterator中只有三个操作,hasNext,next,remove,如果想进行添加,这个迭代器就不可以使用了,这时,可以使用一个新的迭代方式,ListIterator列表迭代器,ListIterator本身也是Iterator的接口,并且有更多的方法。 

既然ArrayList底层是数组实现的,数组的长度是固定的,那么究竟是如何实现的呢? 
ArrayList内部封装了一个默认长度为10的数组。当超出长度时,集合内部会自动生成一个新的数组,然后将原来的数组复制到新的数组中去。这就是导致ArrayList修改数据的效率比LinkedList慢的原因。 

新的数组到底多长? 
ArrayList 50%延长 
Vector 100%延长 

ArrayList判断元素是否相同,底层依据的是元素的equals方法。无论是contains还是remove都会去使用equals判断元素是否相同。所以在往ArrayList集合存储自定义元素时,最好建立该元素特有判断对象是否相同的依据。也就是需要覆盖equals方法。 

LinkedList的特有方法: 
1, 
addFirst(); 
addLast(); 
在jdk1.6以后。 
被offerFirst(); 
offerLast();替代 
2, 
getFirst(); 获取元素,集合的长度不改变 
getLast(); 
在jdk1.6以后,被 
peekFirst(); 
peekLast();替代。 
3, 
removeFirstt();获取元素,集合的长度改变 
  如果集合中没有元素,那么该方法会发生异常NoSuchElementException 
removeFirst(); 
在jdk1.6以后,被 
pollFirst(); 
pollLast();代替。 
如果集合元素没有,不会抛异常,而是返回null 


Vector:vector中提供了一个独特的取出方式,就是枚举 
Enumeration。此接口Enumeration的功能与Iterator接口的功能是重复的。 
Enumeration的名称和方法的名称过程,书写比较麻烦,并且Iterator有个移除的方法。 
所以被Iterator取 代。 


**如果在编译的时候如果出现了 

注意:TestArrayList.java 使用了未经检查或不安全的操作。 
注意:要了解详细信息,请使用 -Xlint:unchecked 重新编译。 

的提醒,并不是编译失败,是因为类型的原因导致的,如果用了泛型,就没有该提示了。 


HashSet: 
HashSet集合元素保证唯一性,依赖的是元素的hashCode方法和equals方法。 
当元素的哈希值不同时,元素都有自己的独立位置。不需要再判断元素的equals方法,当元素的哈希值相同时,这时元素在哈希表中位置相同,这时就需要再判断元素的内容是否相同,就需要调用元素的equals方法进行一次比较。如果equals返回true,那么视为两个元素重复。只存储一个。如果返回false,那么这两个元素不是重复元素,会存储在同一个哈希值上。 
为了建立自定义对象判断元素是否重复的依据。 
需要覆盖hashCode方法,和equals方法。 
而且最好依据对象的特有条件来建立hashcode和equals的实现。 

ArrayList:判断包含,以及删除,都是依据元素的equals方法。 
HashSet:判断包含,以及删除,都是依据元素的hashCode方法。当hashCode值相同时,再判断一次equals方法。 


TreeSet: 
TreeSet集合是用于给元素进行排序的。那么自定义元素本身不具备比较性,treeSet集合是无法对元素进行排序的。所以,在自定义对象时,需要对象具备一个扩展功能,用于比较的,而java已经提供了接口,可以让实现它的对系那个具备比较性。 
那么自定义类,要想被treeSet排序,就要实现Comparable接口,以具备比较功能。 

也可以将一个实现了Comparato接口的子类对象作为参数传递给TreeSet集合的构造函数即可,这样该集合就具备了比较功能。 

TreeSet排序方式有两种: 
1,让元素自身具备比较性 
  其实是让元素实现Comparable接口,覆盖CompareTo方法。这称为元素的自然排序。 
2,当元素自身不具备比较性,或者元素具备的比较性不是所需要的,定义一个比较器。 
  其实就是定义一个类,实现Comparator接口,覆盖Compare方法。将comparator接口的子类对象作为参数传递给TreeSet的构造函数。 

当元素自身具备比较性,同时TreeSet集合也具备比较器,这时以比较器为主。 
所以建议使用第二种方法,比较灵活。 

**一般在描述一个对象时,如果该对象封装了具体的数据,会出现很多这样的对象,这时就需要进行容器的存储。 

那么描述该类对象时,一般要复写几个方法: 
1,hashCode() 
2,equals() 
3,toString() 
4,最好实现Comparable接口让该类具备自然排序功能。 

建立对象自身判断是否相同的依据,同时让对象具备基本的比较性。 



泛型: 
泛型其实就是jdk1.5版本以后出现的一个安全机制。,泛型其实是给编译器使用的。 
Al.add(3);//al.add(new Integer(3));因为jdk1.5之后有自动装箱,所以会自动把3装成对象。 

集合中存储了不同类型的对象,取出时,容易在运行时时期发生classCastException类型转换异常。为了避免这个问题的发生,如果在存储的时候名气了集合要操作的数据类型,这样取就没有问题了。 
就需要在定义集合时,就立刻明确元素的类型。 
其实借鉴于数组。 
可以通过<>来明确元素的类型。 
ArrayList<String> al = new ArrayList<String>(); 

泛型的好处: 
1,将运行时期出现的classCastException问题,转移到编译时期。 
2,泛型的出现避免了强制转换的麻烦。 

泛型的表现形式是<>;

什么时候使用泛型? 
只要用到的类或者接口的旁边有<>的时候,就要明确具体类型。 
泛型的使用其实就是给<>传递实际参数,而这个参数就是一个具体的引用数据类型。 

什么时候使用泛型类? 
当类要操作的引用数据类型不确定的时候,可以使用泛型来定义。 
也就是定义一个类型参数。 
具体要操作什么类型的对象,有使用该类的使用者来明确,将具体的类型作为实际参数传递给<>; 

Class Tool<Q> 

Private Q obj; 
Public void setObj(Q obj){ 
  This.obj = obj; 

Public Q getObj(){ 
  Return obj; 



当泛型定义在类上,该泛型作用于整个类。当该建立对象时,就明确了具体类型。那么凡是使用了类上定义的泛型的方法,操作的类也就固定了。 
当希望类中的方法,可以操作任意类型而不受类中泛型限制。 
可以将泛型定义在方法上。泛型方法。 
Public <Q> void print(Q q){ 
   System.out.println(“print”+q); 



当类中定义static方法时,静态方法是不可以直接访问类上的泛型。因为类上的泛型只有通过建立对象才能明确具体类型。 
所以静态方法如果操作的引用数据类型不确定,只能将泛型定义在方法上。 
(静态方法上定义泛型,必须定义在static关键字之后) 
Public static<W> void method(W w){ 
System.out.prinln(method+”w”); 


当方法中操作的应用数据类型不确定,而且和对应的对象执行的类型也不一定一致。 
这时就将泛型定义在方法上。 


泛型接口: 
1, 
Class InterImpl implements Inter<String>{ 
Public void show(String s){ 
  System.out.println(s); 


2, 
Class InterImpl<T> implements Inter<T>{ 
Public void show(T t){ 
  System.out.println(“show”+t); 



泛型:通配符?代表任意类型。 
Public static void show(Collection<?> coll){ 
Iterator<?> it = coll.iterator(); 
While(it.hasNext()){ 
  System.out.println(it.next()); 




定义集合时要保证左右两边的类型一致。两边只有一边定义泛型,也是可以支持,至少新老版本兼容,但是一样会出现提示信息。 

泛型限定: 
定义T只能固定一种类型,定义?可以是任意类型,只想操作该类或该类的子类。要使用泛型限定。 
? extends E:接收E类型或者E的子类型。 
? super E:接收E类型或者E类型的父类型。 
例如: 
Public static void show(Collection<? extends Person> coll){ 
  Iterator<? extends Person> it = coll.itrator(); 
  While(it.hasNext()){ 
    System.out.println(it.next()); 
  } 


集合框架中的另一个顶层接口:Map 
Map集合的特点: 
1,是一个双列集合,collection是单例集合 
2,Map一次存一对元素,同时键值对的形式。建和值有对应关系,collection是一次存一个元素。 
3,Map集合必须保证集合中键的唯一性。 

Map集合中常见的功能: 
1,添加 
v  put(k , v);将k和v作为元素存储如map集合,当存入相同的k时, 
新的值会覆盖原来的值,并返回原来的值。 
void  putAll(map); 
2,删除 
Clear(); 
V remove(k):按照键删除,并返回被删除的键对应的值。 
3,判断 
Boolean containsKey(Object key) 
Boolean containsValue(Object value); 
Boolean isEmpty(); 
4,获取
Int size();获取map集合的元素个数 
V get(k);通过键获取值。 
Collection values();获取map集合中所有的值。 
Set keySet():获取map集合中所有的键。 
Set entrySet():获取的是键值的映射关系,将映射关系存入到set集合中。 

Map集合没有迭代器。迭代器是collection集合具备的,Map集合的取出元素的原理就是将map集合先转成set集合,然后再进行迭代。 

Map 
  |--Hashtable:底层是哈希表数据结构(同步的,不允许null作为键,null作为值) 
    |--properties:用于配置文件的定义和操作,使用频率非常高,同时键和值都是字符串。是集合中可以和IO技术相结合的对象。 
  |--HashMap:底层是哈希表数据结构(不同步的,允许null作为键,允许null作为值) 
    |--LinkedHashMap:可以保证hashMap存入顺序和取出顺序一致 
  |--TreeMap:可以用来对map集合中的键进行排序。 


如何获取集合中的所有元素? 
一: 
1,获取所有的键,再通过对所有的键进行遍历,在遍历中通过get方法获取每一个键的对应的值,使用keySet方法获取map集合中键的集合。 
2,对set集合进行迭代器的操作。 
Set<Integer> keySet = hm.keySet(); 
Iteraror it = keySet.itertor(); 
While(it.hasNext()){ 
Integer i = it.next(); 
String s = hm.get(i); 
System.out.println(s+”...”+i); 

二: 
1,将map集合中的键值关系取出,并封装成一个键值关系对象,再存储到一个set集合中。 
键值映射关系封装对象后的数据类型是map.entry(内部接口) 
Entry就是一个map接口中的内部静态接口。 
作为一个键值关系的数据类型存在。 
使用entrySet获取map集合中的所有键值。 
Set<Map.Entry<Integer,String>> entry = hm.entrySet(); 
Iterator<Map.Enrty<Integer,String>> it = entrySet.iterator(); 
While(it.hasNext()){ 
Map.Entry<Integer,String> me = it.next(); 
Integer key = me.getKey(); 
String value = me.getValue(); 
System.out.println(key+”...”+value); 



hashMap:参考HashSet 
存入的对象是无序的,想要保证有序。可以使用子类LinkedHashmap。存入和取出的顺序是一致的。 

TreeMap:参考TreeSet 


Utility:集合中的工具类: 
如果有一堆元素。 
要是元素重复,选择ArrayList集合,但是想要让元素按顺序排序,可以使用集合中的工具类。 

工具类有两种。collections,Arrays 
这两个工具类的特点:类中的方法都是静态的,不需要创建对象。 


1,collections 
Collections是集合对象的工具类,提供了操作集合的工具方法。 

Fill(al,”kk”);将集合中的元素替换成指定的元素kk。 

Reverse(al);集合元素反转,头尾对调 

Swap(al,2,4);对集合中的元素进行位置的置换 

Shuffle(al);对集合中的元素进行随机的位置置换 

toArray();将集合变成数组 
给toArray方法传递一个指定类型的数组。 
当指定的长度小于集合的长度,该方法会自动创一个该类型的新数组长度和集合长度一致。用于存储集合中的元素,如果指定的数组长度大于集合的长度,那么该方法就不会创建新数组,而是使用传递进来的数组,存储完集合的元素后,其他的未存储的位置为null。 
将集合变成数组后,限定了对元素的增删操作。 

Xxx synchronizedList(xxx);可以将一个不同步的集合转成一个同步的list集合 

2,Arrays 
Arrays:是数组的工具类,提供了堆数组的工具方法。 
asList(arr);将数组变成集合。 
当数组变成集合后,就可以使用集合的方法来操作数组,而不用自己再定义指针进行数组的操作。(有些方法不可以用,只要是改变集合长度的方法都不能用,因为数组是固定长度的) 

当数组中的元素时引用数据类型时,变成集合后,就将数组中的元素作为集合中的元素存在,当数组中的元素时基本数据类型时,变成集合后,会将这个数组变成集合中的元素。 
List<int[]> list = Arrays.asList(arr); 
System.out.println(list); 
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值