Java类集引出
类集实际上就属于动态对象数组,在实际开发之中,数组的使用出现的机率并不高,因为数组本身有一个最大的缺陷:数组长度是固定的。由于此问题的存在,从JDK1.2开始,Java为了解决这种数组长度问题,提供了动态的对象数组实现框架–Java类集框架。
Java集合类框架实际上就是java针对于数据结构的一种实现。实际上链表的实现就是参考Java集合类实现的。
Collection
在Java的类集里面(java.util包)提供了两个最为核心的接口:Collection
、Map
接口。其中Collection接口的操作形式与链表的操作形式类似,每一次进行数据操作的时候只能够对单个对象进行处理;Map接口针对键值对对象处理。
Collection接口的定义如下:
public interface Collection<E> extends Iterable<E>
从JDK1.5开始发现Collection接口上追加有泛型应用,这样的直接好处就是可以避免ClassCastException
,里面的所有数据的保存类型应该是相同的。在JDK1.5之前Iterable
接口中的iterator()
方法是直接在Collection接口中定义的。此接口的常用方法有如下几个:
在开发之中如果按照使用频率来讲:add()
、iterator()
方法用到的最多。
add():向集合中添加元素
iterator():取得集合的迭代器(遍历集合的工具)
需要说明的一点是,我们很少会直接使用 Collection接口,Collection接口只是一个存储数据的标准,并不能区分存储类型。例如:要存放的数据需要区分重复与不重复。在实际开发之中,往往会考虑使用Collection接口的子接口:List(允许数据重复)、Set(不允许数据重复)。
List接口
List接口概述
首先来观察List接口中提供的方法,在这个接口中有两个重要的扩充方法 :
public E get(int index):根据索引取得数据
public E set(int index,E element):根据指定索引修改相应元素
List子接口与Collection接口相比最大的特点在于其有一个get()
方法,可以根据索引取得内容。由于List本身还是接口,要想取得接口的实例化对象,就必须有子类,在List接口下有三个常用子类:ArrayList
、Vector
、LinkedList
。
ArrayList子类
- 接口中要保存自定义类对象,自定义类必须覆写
equals()
。类集contains()、remove()等方法需要调用equals()来判断元素是否相等。
package www.bit.Collection;
import java.util.ArrayList;
import java.util.List;
class Person {
private String name;
private int age;
@Override
public boolean equals(Object obj) {
if (obj == this){
return true;
}
if (obj == null){
return false;
}
if (!(obj instanceof Person)){
return false;
}
Person person = (Person)obj;
return this.age == person.age && person.name.equals(this.name);
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Test {
public static void main(String[] args){
List<Person> list = new ArrayList<>();
Person person1 = new Person("周润发",50);
Person person2 = new Person("周星驰",40);
Person person3 = new Person("成龙",45);
list.add(person1);
list.add(person2);
list.add(person3);
System.out.println(list.contains(new Person("周星驰",40)));
System.out.println(list.size());
}
}
- 底层实现是一个对象数组,声明一个ArrayList对象时,长度为0。当数组长度不够用时,扩容策略为变为原来数组的1.5倍。
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* Shared empty array instance used for default sized empty instances. We
* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
* first element is added.
*/
transient Object[] elementData; // non-private to simplify nested class access
/**
* Shared empty array instance used for default sized empty instances. We
* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
* first element is added.
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
- 对集合的增删改查都是异步处理,性能较高,线程不安全。
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
Vector
- 底层实现是一个对象数组,声明一个Vector对象时,初始化对象数组长度为10。当数组长度不够用时,扩容策略变为原来的2倍。
/**
* Constructs an empty vector so that its internal data array
* has size {@code 10} and its standard capacity increment is
* zero.
*/
public Vector() {
this(10);
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
- 对集合的修改都采用同步处理(直接在方法上使用内建锁),性能较低,线程安全。
/**
* Appends the specified element to the end of this Vector.
*
* @param e element to be appended to this Vector
* @return {@code true} (as specified by {@link Collection#add})
* @since 1.2
*/
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
请解释ArrayList
与Vector
的区别:
-
产生版本:Vector是JDK1.0产生,ArrayList是JDK1.2产生
-
线程安全:Vector采用在方法添加synchronized来保证线程安全,性能较低;ArrayList采用异步处理,性能较高,线程不安全
-
初始化以及扩容策略:Vector对象产生时就初始化大小为10,当数组不够用时,扩容为原数组的2倍;ArrayList使用懒加载策略,在第一次添加元素时才初始化数组大小,当数组不够用时,扩容为原数组的1.5倍。
LinkList
- 基于链表实现的动态数组
/**
* Links e as first element.
*/
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
/**
* Inserts the specified element at the specified position in this list.
* Shifts the element currently at that position (if any) and any
* subsequent elements to the right (adds one to their indices).
*
* @param index index at which the specified element is to be inserted
* @param element element to be inserted
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
/**
* Links e as last element.
*/
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
- ArrayList封装的是数组;LinkedList封装的是链表。ArrayList时间复杂度为1,而LinkedList的复杂度为n
Set集合接口
Set接口与List接口最大的不同在于Set接口中的内容是不允许重复的。同时需要注意的是,Set接口并没有对 Collection接口进行扩充,而List对Collection进行了扩充。因此,在Set接口中没有get()
方法。
在Set子接口中有两个常用子类:HashSet
(无序存储)、TreeSet
(有序存储)
HashSet无序存储
-
底层基于哈希表
-
不允许元素重复并且无序存储(根据哈希码保存元素),允许存放null
package www.bit.Collection;
import java.util.HashSet;
import java.util.Set;
public class SetTest {
public static void main(String[] args) {
Set<String> set = new HashSet<>();
set.add("SetTest");
set.add("hello");
set.add("China");
set.add(null);
set.add("hello");
set.add("world");
set.add(null);
System.out.println(set);
}
}
运行结果:
[null, world, China, hello, SetTest]
Process finished with exit code 0
TreeSet有序存储
-
底层基于红黑树
-
不允许元素重复并且按照升序排序,不允许存放null
package www.bit.Collection;
import java.util.Set;
import java.util.TreeSet;
public class SetTest {
public static void main(String[] args) {
Set<String> set = new TreeSet<>();
set.add("Banana");
set.add("Pear");
set.add("Apple");
set.add("Watermelon");
set.add("Apple");
System.out.println(set);
}
}
运行结果:
[Apple, Banana, Pear, Watermelon]
Process finished with exit code 0