集合
集合是一种存储对象的容器,也是最为常用的一种存储对象的方式。在集合中可以存储任何类型的对象,且其长度可变。有时因为在程序中可能无法预先知道需要多少个对象,若用数组来存储对象,数组的长度不好定义,而集合的出现就解决了这样的问题。查看api 文档Collection在在java.util 中(注意是大写Collection)。
集合和数组的区别
数组:存储同一种数据类型的集合容器。
集合:存储不同类型对象的容器。
数组和集合类都是容器。数组的长度是固定的,而集合的长度是可变的。数组可以存储基本数据类型,集合中可以存储任意类型的对象。
集合的特点:用来存储对象,且其长度是可以变得,而且可以存储不同类型的对象。
数组的特点:
- 只能存储同一种数据类型的数据。
- 一旦初始化,其长度就固定了。
- 数组中元素和元素之间的地址是连续的。
注意:Object类型的数组是可以存储任意类型的数据的。
集合比数组的优势:
- 集合可以存储任意类型的对象的数据,而数组只能存储同一种数据类型的数据。
- 集合的长度是可以发生变化的,但是数组的长度是固定的。
集合的分类
---|Collection: 单列集合
---|List: 有存储顺序, 可重复
---|ArrayList: 数组实现, 查找快, 增删慢。由于底层是维护了一个Object数组实现, 在增和删的时候会牵扯到数组,因此增容以及拷贝元素比较慢。但是数组是可以直接按索引查找, 所以查找时较快。什么时候使用ArrayList: 如果目前的数据是查询比较多,增删比较少的时候,那么就使用ArrayList存储这批数据。比如:高校的图书馆。ArrayList底层维护了一个Object[] 用于存储对象,默认数组的长度是10。可以通过 new ArrayList(20)显式的指定用于存储对象的数组的长度。当默认的或者指定的容量不够存储对象的时候,容量自动增长为原来的容量的1.5倍。
---|LinkedList: 链表实现, 增删快, 查找慢。由于链表实现, 增加时只要让前一个元素记住自己就可以, 删除时让前一个元素记住后一个元素, 后一个元素住前一个元素. 这样的增删效率较高但查询时需要一个一个的遍历, 所以效率较低。
---|Vector: 和ArrayList原理相同, 但线程安全, 效率略低和ArrayList实现方式相同, 但考虑了线程安全问题, 所以效率略低。
---|Set: 无序, 不可重复
---|HashSet:线程不安全,存取速度快。底层是以哈希表实现的。特点:存取速度快。哈希表边存放的是哈希值。HashSet存储元素的顺序并不是按照存入时的顺序(和List显然不同) 是按照哈希值来存的,所以取数据也是按照哈希值来取得。
---|TreeSet:如果元素具备自然顺序的特性,那么就按照元素自然顺序的特性进行排序存储。红-黑树的数据结构,默认对元素进行自然排序(String)。如果在比较的时候两个对象返回值为0,那么元素重复。
---|LinkedHashSet:会保存插入的顺序。
---| Map: 键值对。Map中的元素是两个对象,一个对象作为键,一个对象作为值。键不可以重复,但是值可以重复。
---|HashMap:采用哈希表实现,所以无序。底层是哈希表数据结构,线程是不同步的,可以存入null键,null值。要保证键的唯一性,需要覆盖hashCode方法,和equals方法。
---|TreeMap:可以对健进行排序。底层是二叉树数据结构。可以对map集合中的键进行排序。需要使用Comparable或者Comparator 进行比较排序。return 0,来判断键的唯一性。
---|HashTable:底层是哈希表数据结构,线程是同步的,不可以存入null键,null值。效率较低,被HashMap 替代。
---|LinkedHashMap:该子类基于哈希表又融入了链表。可以Map集合进行增删提高效率。
注意:
- 出现这么多集合容器的原因是:每一个容器对数据存储方式不同。而这样的存储方式就叫做:数据结构。
- 集合和数组中存放的都是对象的引用。
- 集合是有序的,集合的有序不是指自然顺序,而是指添加进去的顺序和元素出来的顺序是一致的。
使用集合的各种情况
Collection |
我们需要保存若干个对象的时候使用集合。 |
List
|
如果我们需要保留存储顺序, 并且保留重复元素, 使用List. 如果查询较多, 那么使用ArrayList 如果存取较多, 那么使用LinkedList 如果需要线程安全, 那么使用Vector |
Set
|
如果我们不需要保留存储顺序, 并且需要去掉重复元素, 使用Set. 如果我们需要将元素排序, 那么使用TreeSet 如果我们不需要排序, 使用HashSet, HashSet比TreeSet效率高. 如果我们需要保留存储顺序, 又要过滤重复元素, 那么使用LinkedHashSet |
集合类
学习集合对象
学习集合时,先学习Collection中的共性方法。因为其共性方法是在多个容器中都可以使用的。若要使用子类集合中的特有方法,可以查看子类的具体内容。
创建集合对象
创建集合对象,使用Collection中的List的具体实现类ArrayList
Collection coll=new Arraylist();
Collection类的共性方法
增加:
1: add(E e) 将指定对象存储到容器中,添加成功返回true,添加 失败返回false.
add 方法的参数类型是Object 便于接收任意对象
2: addAll(Collection c) 将指定集合中的元素添加到调用该方法和集合中
删除:
3: clear()
4: remove(Object o) 将指定的对象从集合中删除
5: removeAll(Collection c) 将指定集合中的元素删除
6: retainAll(Collection c)
查看
7: size()
判断
8: isEmpty() 判断集合是否为空
9: contains(Object 0) 判断集合何中是否包含指定对象
10:containsAll(Collection<?> c) 判断集合中是否包含指定集合
10:equals()判断两个对象是否相等
迭代:
11:int size() 返回集合容器的大小
12: toArray() 集合转换数组
List集合特有方法
List操作的方法都有索引值。
增加
void add(int index, E element) 指定位置添加元素
boolean addAll(int index, Collection c) 指定位置添加集合
删除
E remove(int index) 删除指定位置元素
修改
E set(int index, E element) 返回的是需要替换的集合中的元素
获取
E get(int index) 注意: IndexOutOfBoundsException
int indexOf(Object o) // 找不到返回-1
lastIndexOf(Object o)
List<E> subList(int fromIndex, int toIndex) // 不包含toIndex
迭代
listIterator()
ArrayList 类特有的方法:
ensureCapacity(int minCapacity)
trimToSize()
LinkedList类特有方法:
addFirst(E e)
addLast(E e)
getFirst()
getLast()
removeFirst()
removeLast()
如果集合中没有元素,获取或者删除元素抛:NoSuchElementException
数据结构
栈
先进后出
push()
pop()
队列
先进先出
offer()
poll()
返回逆序的迭代器对象:descendingIterator() 返回逆序的迭代器对象
ArrayList 和 LinkedList的优缺点
1、ArrayList 是采用动态数组来存储元素的,它允许直接用下标号来直接查找对应的元素,但是插入元素要涉及数组元素移动及内存的操作。总结:查找速度快,插入操作慢。
2、LinkedList 是采用双向链表实现存储,按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快
问题:有一批数据要存储,要求存储这批数据不能出现重复数据,ArrayList、LinkedList都没法满足需求。
解决办法:使用 set集合。
Vector
Vector: 描述的是一个线程安全的ArrayList。
笔试题: 说出ArrayLsit与Vector的区别?
相同点: ArrayList与Vector底层都是使用了Object数组实现的。
不同点:
1. ArrayList是线程不同步的,操作效率高。Vector是线程同步的,操作效率低。
2. ArrayList是JDK1.2出现,Vector是jdk1.0的时候出现的。
ArrayList: 单线程效率高
Vector : 多线程安全的,所以效率低
特有的方法
void addElement(E obj) 在集合末尾添加元素
E elementAt( int index) 返回指定角标的元素
Enumeration elements() 返回集合中的所有元素,封装到Enumeration对象中
Enumeration 接口:
boolean hasMoreElements() 测试此枚举是否包含更多的元素。
E nextElement() 如果此枚举对象至少还有一个可提供的元素,则返回此枚举的下一个元素。
Set
Set:注重独一无二的性质,该体系集合可以知道某物是否已近存在于集合中,不会存储重复的元素。用于存储无序(存入和取出的顺序不一定相同)元素,值不能重复。
对象的相等性
引用到堆上同一个对象的两个引用是相等的。如果对两个引用调用hashCode方法,会得到相同的结果,如果对象所属的类没有覆盖Object的hashCode方法的话,hashCode会返回每个对象特有的序号(java是依据对象的内存地址计算出的此序号),所以两个不同的对象的hashCode值是不可能相等的。
如果想要让两个不同的Person对象视为相等的,就必须覆盖Object继下来的hashCode方法和equals方法,因为Object hashCode方法返回的是该对象的内存地址,所以必须重写hashCode方法,才能保证两个不同的对象具有相同的hashCode,同时也需要两个不同对象比较equals方法会返回true
该集合中没有特有的方法,直接继承自Collection。
HashSet
HashSet:底层是使用哈希表来支持的。特点:存取速度快。哈希表边存放的是哈希值。HashSet存储元素的顺序并不是按照存入时的顺序(和List显然不同) 是按照哈希值来存的,所以取数据也是按照哈希值来取得。
由于Set集合是不能存入重复元素的集合。那么HashSet也是具备这一特性的。HashSet如何检查重复?HashSet会通过元素的hashcode()和equals方法进行判断元素师否重复。如果对象的hashCode值是不同的,那么HashSet会认为对象是不可能相等的。
因此我们自定义类的时候需要重写hashCode,来确保对象具有相同的hashCode值。
如果元素(对象)的hashCode值相同,会继续使用equals 进行比较.如果 equals为true 那么HashSet认为新加入的对象重复了,就会加入失败。如果equals 为false那么HashSet 认为新加入的对象没有重复.新元素可以存入.
总结:往Haset添加元素的时候,HashSet会先调用元素的hashCode方法得到元素的哈希值,然后通过元素的哈希值经过移位等运算,就可以算出该元素在哈希表中的存储位置。如果算出元素存储的位置目前没有任何元素存储,那么该元素可以直接存储到该位置上。如果算出该元素的存储位置目前已经存在有其他的元素了,那么会调用该元素的equals方法与该位置的元素再比较一次,如果equals返回的是true,那么该元素与这个位置上的元素就视为重复元素,不允许添加,如果equals方法返回的是false,那么就允许添加该元素。
那么该元素运行添加。哈希值相同equals为false的元素是怎么存储呢,就是在同样的哈希值下顺延(可以认为哈希值相同的元素放在一个哈希桶中)。也就是哈希一样的存一列。
HashSet:通过hashCode值来确定元素在内存中的位置。一个hashCode位置上可以存放多个元素。
当hashcode() 值相同equals() 返回为true 时,hashset 集合认为这两个元素是相同的元素.只存储一个(重复元素无法放入)。调用原理:先判断hashcode 方法的值,如果相同才会去判断equals 如果不相同,是不会调用equals方法的。
问题:HashSet到底是如何判断两个元素重复的呢?
答:通过hashCode方法和equals方法来保证元素的唯一性,add()返回的是boolean类型判断两个元素是否相同,先要判断元素的hashCode值是否一致,只有在该值一致的情况下,才会判断equals方法,如果存储在HashSet中的两个对象hashCode方法的值相同equals方法返回的结果是true,那么HashSet认为这两个元素是相同元素,只存储一个(重复元素无法存入)。
注意:HashSet集合在判断元素是否相同先判断hashCode方法,如果相同才会判断equals。如果不相同,是不会调用equals方法的。
HashSet 和ArrayList集合都有判断元素是否相同的方法,HashSet使用hashCode和equals方法,ArrayList使用了equals方法。
treeSet要注意的事项
1. 往TreeSet添加元素的时候,如果元素本身具备了自然顺序的特性,那么就按照元素自然顺序的特性进行排序存储。
2. 往TreeSet添加元素的时候,如果元素本身不具备自然顺序的特性,那么该元素所属的类必须要实现Comparable接口,把元素的比较规则定义在compareTo(T o)方法上。
3. 如果比较元素的时候,compareTo方法返回的是0,那么该元素就被视为重复元素,不允许添加.(注意:TreeSet与HashCode、equals方法是没有任何关系。)。
4. 往TreeSet添加元素的时候, 如果元素本身没有具备自然顺序 的特性,而元素所属的类也没有实现Comparable接口,那么必须要在创建TreeSet的时候传入一个比较器。
5. 往TreeSet添加元素的时候,如果元素本身不具备自然顺序的特性,而元素所属的类已经实现了Comparable接口, 在创建TreeSet对象的时候也传入了比较器,那么是以比较器的比较规则优先使用。
自定义定义比较器: 自定义一个类实现Comparator接口即可,把元素与元素之间的比较规则定义在compare方法内即可。
自定义比较器的格式 :
class 类名 implements Comparator{
}
推荐使用:使用比较器(Comparator)。
原因:如果使用comparable接口,那么只能在定义的这个类内部使用这个比较器; 但是如果使用comparable借口,可以在其他类中使用这个比较器。
红-黑树算法
红黑树是一种特定类型的二叉树。
红黑树算法的规则: 左小右大。
再次强调:当Comparable比较方式和Comparator比较方式同时存在时,以Comparator的比较方式为主;
注意:在重写compareTo或者compare方法时,必须要明确比较的主要条件相等时要比较次要条件。
问题:为什么使用TreeSet存入字符串,字符串默认输出是按升序排列的?
答:因为字符串实现了一个接口,叫做Comparable 接口.字符串重写了该接口的compareTo 方法,所以String对象具备了比较性.那么同样道理,自定义元素(例如Person类,Book类)想要存入TreeSet集合,就需要实现该接口,也就是要让自定义对象具备比较性.
存入TreeSet集合中的元素要具备比较性。
比较性要实现Comparable接口,重写该接口的compareTo方法。
TreeSet属于Set集合,该集合的元素是不能重复的,TreeSet如何保证元素的唯一性。
通过compareTo或者compare方法中的来保证元素的唯一性。
添加的元素必须要实现Comparable接口。当compareTo()函数返回值为0时,说明两个对象相等,此时该对象不会添加进来。
Map
Map一次存一对元素, Collection 一次存一个。Map 的键不能重复,保证唯一。
Map 一次存入一对元素,是以键值对的形式存在.键与值存在映射关系.一定要保证键的唯一性.。
interface Map<K,V>:K - 此映射所维护的键的类型;V - 映射值的类型
常见方法
添加:
1、V put(K key, V value) (可以相同的key值,但是添加的value值会覆盖前面的,返回值是前一个,如果没有就返回null)
2、putAll(Map<? extends K,? extends V> m) 从指定映射中将所有映射关系复制到此映射中(可选操作)。
删除:
1、remove() 删除关联对象,指定key对象
2、clear() 清空集合对象
获取:
1:value get(key); 可以用于判断键是否存在的情况。当指定的键不存在的时候,返回的是null。
判断:
1、boolean isEmpty() 长度为0返回true否则false
2、boolean containsKey(Object key) 判断集合中是否包含指定的key
3、boolean containsValue(Object value) 判断集合中是否包含指定的value
4、Int size() 长度
Collections与Arrays
Collections
常见方法:
- 对list进行二分查找:
前提该集合一定要有序。
int binarySearch(list,key);
//必须根据元素自然顺序对列表进行升级排序
//要求list 集合中的元素都是Comparable 的子类。
int binarySearch(list,key,Comparator);
2,对list集合进行排序。
sort(list);
//对list进行排序,其实使用的事list容器中的对象的compareTo方法
sort(list,comaprator);
//按照指定比较器进行排序
3,对集合取最大值或者最小值。
max(Collection)
max(Collection,comparator)
min(Collection)
min(Collection,comparator)
4,对list集合进行反转。
reverse(list);
5,对比较方式进行强行逆转。
Comparator reverseOrder();
Comparator reverseOrder(Comparator);
6,对list集合中的元素进行位置的置换。
swap(list,x,y);
7,对list集合进行元素的替换。如果被替换的元素不存在,那么原集合不变。
replaceAll(list,old,new);
8,可以将不同步的集合变成同步的集合。
Set synchronizedSet(Set<T> s)
Map synchronizedMap(Map<K,V> m)
List synchronizedList(List<T> list)
9. 如果想要将集合变数组:
可以使用Collection 中的toArray 方法。注意:是Collection不是Collections工具类
传入指定的类型数组即可,该数组的长度最好为集合的size。
Arrays
常用方法
binarySearch(int[]) 二分查找,数组需要有序
binarySearch(double[]) 二分查找,数组需要有序
sort(int[]) 数组排序
sort(char[]) 数组排序
toString(int[]):将数组变成字符串。
copyOf(): 复制数组。
copyOfRange():复制部分数组。
equals(int[],int[]):比较两个数组是否相同。
List asList(T[]):将数组变成集合。这样可以通过集合的操作来操作数组中元素,但是不可以使用增删方法,add,remove。因为数组长度是固定的,会出现UnsupportOperationExcetion。
可以使用的方法:contains,indexOf。
如果数组中存入的基本数据类型,那么asList会将数组实体作为集合中的元素。如果数组中的存入的引用数据类型,那么asList会将数组中的元素作为集合中的元素。