Java容器

162 阅读10分钟

概述:和StringBuilder特性差不多,动态长度,可以放不同类型



最顶层:Collection接口,下面两个 List和 Set接口,下面有着自己的实现类

集合里面存储对象在内存里面是以地址值的方式来进行的。

ArrayList  增删判断查(好像没有改的方法)

retainAll()取交集,然后回改变原来的集合内容。

removeALL() 去掉交集。

元素的取出:

迭代器:Iterator it=数组列表对象.iterator()


LIst特点:有序,可重复,

Set特点:无序,不可重复


List的坑:

1. 迭代器不能和集合方法混着用会造成并发程序安全问题

    for(iterator i=a.iterator();i.hasNext();)

{

    a.remove()//这样会报错的

}

如果在迭代器当中删除了某个元素,虽然集合的引用删除了,但是内存当中还是有这个对象。你可以把obj打印出来,但是集合里面增加和删除都会对应的变化

迭代器只有三个动作:

1.删除2.判断3.下一个

ListIterator() ,Iterator 的子接口,然后方法多了很多

比如迭代器里面里面的增删改查方法,对前面元素的操作等,

List 的三个子类


1.Arraylist: 底层数组增删很慢,改查很快

2.LinkedList 底层链表改查很慢,增删很快

3.Vector:地层是数组


1和3的区别:1不同步,效率高,1.2版本后出现,初始大小都是10,1增加之后比较节约空间

查找很慢,修改很快


linkedList 特有方法: 最前最后的元素的增加,获取,删除

removeFirst()删除元素,可以返回被删除的元素,get()只是返回,不删除(get对应不会报错的方法,peekFirst(),获取但是不返回也不报错的那种)

这个的增强方法,popFirst(),返回并且删除,但是如果为空的话就不会报错只会返回为空

判断集合总是否包含集合,可以用COntains方法。


判断元素是否相同。重写equals方法

contains 和 remove方法底层会自动调用equals()方法。


Set这一类:

Set功能和Collection功能差不多,取出也是用的迭代器。无序,不重复

    子类:

  1. HashSet
  2. TreeSet

HashSet类别,底层是Hash表,先判断容器类不的对象的Hash值是否相同,不相同的话就找个位置重新放下。相同的话需要在用euqals方法判断是否是同一个对象。

判断是否存在和remove()依赖于 Hashcode() 在依赖 contains()方法


TreeSet;类别:增删改查4

特点:可以对Set里面进行排序

测试,自定义的。

Camparable接口:实现这个类可以使自定义类具有比较的功能。重写compareTo()方法(注返回值为0的话,则说明是同一对象,存不进去)

第二种排序方式:当元素不具有或者具备的比较性不适用,可以让集合自身具有比较性

一初始化就有比较方式,在构造函数放比较器Comparator接口

覆盖compare方法()

当两种比较方式都有的话,比较器的优先级较高

底层结构:二叉树结构。比较,删除,判断对treeSet方法来说都是依据compareTo()的方法


泛型概述:编译时期确定参数的类型,把运行时期的错误能够在编译期间解决

注意集合标注了之后,迭代器也要标注(感觉容器都需要标注出来)

像数组的框一样,在后面写个<String/其他类型>左边和右边都需要写,用来接收数据类型的。可用于容器,比较器。

Comparator接口也可以加上泛型,

class a implements Comparator<String>

{

compare(String o1,String o2)

}

euquals()没泛型,别乱写

自定义泛型类:

由调用方设定参数类型。可以放在类上,方法上,成员变量上

类的定义 上写定义,调用的时候,具体写清楚是哪个类型就可以。


泛型定义在类上,该类型的成员函数,成员变量变量类型都已经确定

方法的返回值前面不用写

泛型定义在方法上:该泛型类型只对该方法有效

格式 

eg: public <T>  void show <T t>

{

}

第三种类型:

泛型类上,泛型方法重置了新的泛型参数类型


静态方法不可以访问类上定义的泛型,如果静态函数的变量类型不确定,可以把变量类型定义泛型在函数上

泛型定义在接口上

interface I<T>

{

void show(T t)}

子类实现的话,可以确定参数,也可以不指定

class Interclass <T> implements I<T>

<T>把T换成?表示通配符,表示可以传任意一种对象,但是不能够将这个对象赋值给某一变量(<?>T= i<T>.next()),也不能使用特定方法。(比如.length()不确定某个参数会不会有该方法)

继承当中的泛型:

原函数假如定义 <People>类型的函数,你传一<Student>类型的参数进去,容器都不匹配,肯定不行呀。理解就是这里不能适用于多态形式

《? extends Person》泛型限定,接受Person及其子类

泛型的通配符:? 未知类型。

 泛型的限定: ? extends E: 接收E类型或者E的子类型对象。

上限 一般存储对象的时候用。比如 添加元素 addAll. ? super E: 接收E类型或者E的父类型对象。 

下限。 一般取出对象的时候用。比如比较器。 


问题:class,接口什么时候可以不用写泛型

eg: Map.Entry<String,String> me=i.next();  感觉之前Iterator<Map.Entry<String,String>都写了,取出来应该不用了吧。  不写又报错,头皮发麻


Map集合 特点,没有迭代器

Map<K,V>接口

Map:一次添加一对元素。Collection 一次添加一个元素。 Map也称为双列集合,Collection集合称为单列集合。 其实map集合中存储的就是键值对。 map集合中必须保证键的唯一性。 常用方法: 1,添加  put2.删除3.判断4.获取

1,添加。 value put(key,value):返回前一个和key关联的值,如果没有返回null. 对于同一个键。后添加的值会覆盖前一个的值,并且返回前一个结果的值

2,删除。 186 void clear():清空map集合。 value remove(key):根据指定的key翻出这个键值对。 

3,判断。 boolean containsKey(key): boolean containsValue(value): boolean isEmpty(); 4,获取。 value get(key):通过键获取值,如果没有该键返回null。 当然可以通过返回null,来判断是否包含指定键。 int size(): 获取键值对的个数。 

一次性获取所有元素的方法:

1.使用KeySet()方法,将Map所有键存入Set集合中,因为他有迭代器。再迭代用get()取出

2. entrySet()方法,存放关系到Set集合里面(Key-value),Map.Entry表示这种关系的数据类型是Map.Entry(步骤,获取装Map.Entry的Set集合,用迭代器,得到Map.Entry,循环里面获得键和值(getKey,和getValue()获取键和值))

Set<Map.Entry>

Map.Entry的方法,获取键,获取值(其实Entry是Map接口的子接口)

类比:结婚证书,而不是丈夫和妻子单独列出来



Map常用的子类:

 |--Hashtable :内部结构是哈希表,是同步的。不允许null作为键,null作为值。无序的

     |--Properties:用来存储键值对型的配置文件的信息,可以和IO技术相结合。 

|--HashMap : 内部结构是哈希表,不是同步的。允许null作为键,null作为值效率高。

 |--TreeMap : 内部结构是二叉树,不是同步的。可以对Map集合中的键进行排序。


Set底层是就是使用了Map

remove(key)删除会吧对应的value返回

values() 返回集合类型。Collections


自定义类的创建,

1.需要重写hashCode和equals()方法(对于HashSet和HashMap),这样能够去除掉重复元素,产生的Item很多的话,最好再重写compareTo()


Collections的辅助方法:

集合操作,大多数是Static,可以对

例如,sort(),可对List进行排序

如果对于没有排序规则的集合,可以自定义完比较器之后再去把比较器作为参数传入静态方法里面()

fill(List,"pp")方法,将集合中的所有元素替换成指定元素

replaceAll(list,老值,新值)

reverse(list)反转

Collections.reverseOrder()返回目前比较器的反序列比较器,这里没有参数;如果是自定义比较器,可以把自定义比较器放在这个参数里面。

可以返回线程同步的集合。

swap(list,1,2)一和二位置置换

shuffel(list)随机打乱

集合的Arrays,用于操作数组进行操作的工具类

静态类 toString(),把数组变成字符串

asList()  把数组变成List集合

数组变集合的特点:

1.集合方法操作数组元素,比如contains()方法。

2.变成集合之后,不能增删,增删会报异常

3.* 如果数组中的元素是对象,那么转成集合时,直接将数组中的元素作为集合中的元素进行集合存储。 * * 如果数组中的元素是基本类型数值,那么会将该数组作为集合中的元素进行存储。

集合变数组: 各个集合类的toArray(里面放需要类型的数组(数组大小太小的话会重新分配,太大的话会多的null,最好放一个list.size()空间分配最优))方法

Collections.toArray()方法

List list = new ArrayList();

 list.add("abc1");

 list.add("abc2");

 list.add("abc3"); 

/* * toArray方法需要传入一个指定类型的数组。

 * 长度该如何定义呢? * 如果长度小于集合的size,那么该方法会创建一个同类型并和集合相同size的数组。

 * 如果长度大于集合的size,那么该方法就会使用指定的数组,存储集合中的元素,其他位置默认为 * null。 

* 所以建议,最后长度就指定为,集合的size。

 */ String[] arr = list.toArray(new String[list.size()]); System.out.println(Arrays.toString(arr));


为何集合要变数组的原因:

1.限定对元素的操作,数组的长度固定,不需增删


增强的for循环:

foreach语句: * 格式: * for(类型 变量 :Collection集合|数组)    (其中,类型表示的是容器里面元素的类型)

支持迭代器的操作都支持高级for循环

 * { * * } * * 传统for和高级for的区别?

 * 传统for可以完成对语句执行很多次,因为可以定义控制循环的增量和条件。

 * * 高级for是一种简化形式。 

* 它必须有被遍历的目标。该目标要是数组,要么是Collection单列集合。 

 * 对数数组的遍历如果仅仅是获取数组中的元素,可以使用高级for。 

 如果要对数组的角标进行操作建议使用传统for。

list.add("abc1");

 list.add("abc2");

 list.add("abc3"); 

for(String s : list)

//简化书写。 System.out.println(s);

 } 

局限性:只能对集合中的元素进行取出,不能修改,因为变量只是表示的一种引用,指向虽然变了,但是集合中的元素没有发生变化。迭代器除了遍历还有remove集合中的元素,如果用listIterator().还能对集合进行增删功能


静态导入:

import static java.util.Collections.*;//静态导入,其实到入的是类中的静态成员。 //import static java.util.Collections.max;//静态导入,其实到入的是类中的静态成员。