集合:有时也被称作容器,是对象的持有者,它们可以以有助于高效访问的方式来储存和组织对象。
集合框架继承关系:
-----------------------------------------------------------------------------------------------------------------------------------------
Collection<E>:是集合框架的根接口。所有的集合都继承Collection。其中定义了集合中常用的方法,如add、remove、size、toArray、Iterator等。
List<E>:集合特点有序,元素可以重复。继承了Collection,增添了许多有关索引的方法。
特有方法。凡是可以操作角标的方法都是该体系特有的方法。
增
add(index,element);
addAll(index,Collection);
删
remove(index);
改
set(index,element);
查
get(index):
subList(from,to);
listIterator();
int indexOf(obj):获取指定元素的位置。
ListIterator listIterator();
ArrayList<E>:底层的数据结构使用的是数组结构。
特点:查询速度很快。但是增删稍慢。线程不同步。是一个不错的基本列表实现,它将其元素存放在一个数组中。它有一个容量,它表示在不需要为更大的数组分配内存的情况下所能容纳的元素数量。当空间用尽时,就必须重新分配一个数组以替代原数组。所以正确地设置初始容量可以提高性能。如果数据的初始大小远远小于最终的大小,那么可以将初始容量设置的更大一点。减少使用更大数组替代原数组的次数。但是如果定义的太大又浪费空间。
ArrayList构造器:
public ArrayList():使用默认容量创建一个新的ArrayList集合;
public ArrayList(int a):定义一个新的ArrayList集合,不用改变就可以存放a个元素;
public ArrayList(Collection<? extends E> coll):创建一个新的ArrayList集合,它的初始内容就是coll的内容。
LinkedList<E>:底层是双向链表。增删速度很快,查询稍慢。线程不同步。
LinkedList特有方法:
addFirst();
addLast();
getFirst();
getLast();
获取元素,但不删除元素。如果集合中没有元素,会出现NoSuchElementException
removeFirst();
removeLast();
获取元素,但是元素被删除。如果集合中没有元素,会出现NoSuchElementException
在JDK1.6出现了替代方法。
offerFirst();
offerLast();
peekFirst();
peekLast();
获取元素,但不删除元素。如果集合中没有元素,会返回null。
pollFirst();
pollLast();
获取元素,但是元素被删除。如果集合中没有元素,会返回null。
Set<E>:集合特点无序,元素不可以重复。继承了Collection,为其方法提供了更具体的契约,但是没有添加新的方法。
常用集合:TreeSet<E>和hashSet<E>。
TreeSet<E>:可以对Set集合中的元素进行排序。
底层数据结构是二叉树。
保证元素唯一性的依据:
compareTo方法return 0.
TreeSet排序的第一种方式:让元素自身具备比较性。
元素需要实现Comparable接口,覆盖compareTo方法。
也种方式也成为元素的自然顺序,或者叫做默认顺序。
TreeSet的第二种排序方式。
当元素自身不具备比较性时,或者具备的比较性不是所需要的。
这时就需要让集合自身具备比较性。
在集合初始化时,就有了比较方式。
hashSet<E>:底层数据结构是哈希表。是线程不安全的。不同步。
HashSet是如何保证元素唯一性的呢?
是通过元素的两个方法,hashCode和equals来完成。
如果元素的HashCode值相同,才会判断equals是否为true。
如果元素的hashcode值不同,不会调用equals。
注意,对于判断元素是否存在,以及删除等操作,依赖的方法是元素的hashcode和
equals方法。
总结:在自定义一个类的时候我们一般要定义类的特有方法,同时我们还有实现Comparable接口的compareTo方法。这样如果将这个类存储在集合中的时候,能够有一个自然顺序。不然会出错。如果我们将这个类存储在hashSet中。那么我们还要覆盖hashCode和equals方法。注意在定义equals方法时不能使用泛型,因为我们的目的是覆盖Object类中的hashCode和equals方法。
例如:
class Student implements Comparable<Student>
{
private String name;
private int age;
Student(String name,int age)
{
this.name=name;
this.age=age;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
public int compareTo(Student s)
{
int num=this.getName().compareTo(s.getName());
if(num==0)
return new Integer(this.getAge()).compareTo(new Integer(s.getAge()));
return num;
}
public boolean equals(Object obj)
{
if(!(obj instanceof Student))
throw new RuntimeException("类型转换异常");
Student s =(Student)obj;
return new String(this.name).equals(s.getName())&&new Integer(this.age).equals(s.getAge());
}
public int hashCode()
{
return this.getName().hashCode()+this.getAge()*7;
}
}
当然如果我们定义了一个TreeSet集合但是我们不想使用自然排序。那么我们就可以定义个一个比较器。比较器的定义就是定义一个类,这个类实现了Comparatot<T>方法。并且覆盖了compare(T o1,T o2)方法。
比如:一个Person类比较器。
class MyCom implements Comparator<Person>
{
public int compare(Person p1,Person p2)
{
int num = new String(p1.getName()).compareTo(new String(p2.getName()));
if(num==0)
return (p1.getAge()-p2.getAge());
return num;
}
}
下面我们来说说比较器和比较方法的原理。通过比较器中的compare和比较方法compareTo的返回值是一个负数、零或者正数。集合中的数据将被存储在一个二叉树的结构中,如果返回负数,那么就会被存储在二叉树的左边,为零说明这个即将存入的这个数是重复的。所以不会被存入(但是在hashMap集合中,如果有存入了键相同,但是值不一样的数据时。那么旧的数据会被覆盖,新的数据会重新存入)。为正这存储在二叉树的右边。
说到集合。我们必须探讨一下迭代器,迭代器为我们提供了一个获取集合中的元素并进行操作的安全途径。
其中Iterator<E>是通用的迭代器。提供了hasNext、next、remove方法。通过:Iterator方法获取;
ListIterator<E>是list类的专用迭代器。他继承了Iterator,并且定义了适合List集合的专有方法:set、add。通过:listIterator方法获取。
比如:
forL(Iterator<Student> it = al.iterator() ;it.hasNext() ; )
{
Student stu = it.next();
System.out.println(stu.gerName());
}
说道迭代器,我们可以展一下增强for语句:
增强for语句又叫做for-each循环:
for( Student stu : al )
{
System.out.println(stu.gerName());
}
注意:当使用增强for语句的时候,我们不可以移除元素。
泛型:JDK1.5版本以后出现新特性。用于解决安全问题,是一个类型安全机制。
好处
1.将运行时期出现问题ClassCastException,转移到了编译时期。
方便于程序员解决问题。让运行时问题减少,安全。
2,避免了强制转换麻烦。
泛型格式:通过<>来定义要操作的引用数据类型。
在使用java提供的对象时,什么时候写泛型呢?
通常在集合框架中很常见,只要见到<>就要定义泛型。其实<> 就是用来接收类型的。当使用集合时,将集合中要存储的数据类型作为参数传递到<>中即可。
泛型可以定义在类上,在整个类中都有效,也可以定义在方法上,只在这个方法有效。但是如果在static方法上使用泛型,必须定义在方法上,不能使用类上的泛型。因为静态是领先于类存在与内存中。
? :通配符。也可以理解为占位符。
泛型的限定:
? extends E: 可以接收E类型或者E的子类型。上限。
? super E: 可以接收E类型或者E的父类型。下限。
泛型的定义方法:
class Tool<T>
{
void show1(T t)
{
//some code;
}
void show2(T t)
{
//some code;
}
static <W> void show2(W t)
{
//some code;
}
<Q> void show3(Q q)
{
//some code;
}
}