单对象保存父接口:Collection
java.util.Collection是进行单对象保存的最大父接口,即每次利用Collection接口都只能保存一个对象信息。单对象保存父接口定义如下:
public interface Collection<E> extends Iterable<E>
由该定义我们可以发现Collection接口中使用了泛型,保证了集合中操作数据的统一,同时Collection接口属于Iterable的子接口。
Collection接口的核心方法
No. | 方法名称 | 类型 | 描述 |
---|---|---|---|
1 | public boolean add(E e) | 普通 | 向集合里面保存数据 |
2 | public boolean addAll(Collection<? extends E> c) | 普通 | 追加一个集合 |
3 | public void clear() | 普通 | 向集合里面保存数据 |
4 | public boolean contains(Object o) | 普通 | 判断是否包含指定的内容,需要equals()支持 |
5 | public boolean isEmpty() | 普通 | 判断是否是空集合(不是null) |
6 | public boolean remove(Object o) | 普通 | 删除对象,需要equals()支持 |
7 | public int size() | 普通 | 取得集合中保存元素个数 |
8 | public Object[] toArray() | 普通 | 将集合变为对象数组保存 |
9 | public Iterator< E > iterator() | 普通 | 为Iterator接口实例化(Iterable接口定义) |
虽然Collection是单对象集合操作的最大父接口,但是Collection接口本身却存在一个问题,即:无法区分保存的数据是否重复。所以在实际的开发中,往往会使用Collection的两个子接口:List子接口(数据允许重复)、Set子接口(数据不允许重复)。
List子接口
List子接口最大的功能是里面保存的数据可以存在重复内容,在Collection子接口中,List子接口是最为常用的一个子接口。
List子接口扩充的方法
No. | 方法名称 | 类型 | 描述 |
---|---|---|---|
1 | public E get(int index) | 普通 | 取得索引编号的内容 |
2 | public E set(int index,E element) | 普通 | 修改指定索引编号的内容 |
3 | public ListIterator< E > listIterator() | 普通 | 为ListIterator接口实例化 |
在使用List接口时可以ArrayList或Vector两个子接口来进行接口对象的实例化操作。
新的子类:ArrayList
例:List基本操作
package Project.Study.ArrayListClass;
import java.util.ArrayList;
import java.util.List;
public class Test1 {
public static void main(String[]args){
//使用了泛型,从而保证集合中所有的数据类型都一致
List<String>all = new ArrayList<>(); //实例化List集合
System.out.println("长度:" + all.size() + ",是否为空:"+all.isEmpty());
all.add("Hello"); //保存数据
all.add("Hello"); //保存重复数据
all.add("World");
System.out.println("长度:"+all.size()+",是否为空:"+all.isEmpty());
//Collection接口定义size()方法取得了集合长度,List子接口扩充get()方法根据索引取得了数据
for (String str : all) { //取得索引数据
System.out.println(str); //直接输出内容
}
}
}
//结果:
//长度:0,是否为空:true
//长度:3,是否为空:false
//Hello
//Hello
//World
上程序通过ArrayList子类实例化List对象,这样就可以使用List接口中定义的方法(包括Collection接口定义的方法)。
例:在集合里面保存对象
package Project.Study.ArrayListClass;
import java.util.ArrayList;
import java.util.List;
class Book{
private String title;
private double price;
public Book(String title,double price){
this.title=title;
this.price=price;
}
@Override
public boolean equals(Object obj){//必须覆写此方法,否则remove()、contains()无法使用
if (this==obj){
return true;
}
if (obj==null){
return false;
}
if (!(obj instanceof Book)){
return false;
}
Book book = (Book)obj;
if (this.title.equals(book.title)&&this.price==book.price){
return true;
}
return false;
}
@Override
public String toString(){
return "书名:"+this.title+",价格:"+this.price+"\n";
}
}
public class Test2 {
public static void main(String[]args){
List<Book>all = new ArrayList<>(); //List接口对象
all.add(new Book("Java",99)); //保存自定义类对象
all.add(new Book("C++",99.9));
all.add(new Book("Python",89));
all.remove(new Book("Java",99)); //删除对象
System.out.println(all);
}
}
//结果:
//[书名:C++,价格:99.9
//, 书名:Python,价格:89.0
//]
Set子接口
Set子接口并不像List子接口一样对Collection接口进行了大量的扩充,而是简单地继承了Collection接口。在Set子接口下有两个常用的子类:HashSet、TreeSet。
- HashSet是散列存放数据;
- TreeSet是有序存放的子类。
关于“Hash”的说明:
在HashSet这个子类上采用了Hash算法(一般称为散列、无序)。采用该Hash算法保存的集合都是无序的,但是其查找速度较快。
HashSet子类
例:观察HashSet子类的特点
package Project.Study.SetClass;
import java.util.HashSet;
import java.util.Set;
public class Test1 {
public static void main(String[]args){
Set<String>all = new HashSet<>(); //实例化Set接口
all.add("Hello"); //保存数据
all.add("World");
all.add("!!!");
all.add("!!!"); //重复数据
System.out.println(all); //直接输出集合
}
}
//结果:
//[!!!, Hello, World]
由上程序我们可以发现,在Set集合里面是不允许保存重复数据的。
像上面的保存的是重复数据的话在IDEA里面是会有提示的,提示这是一个重复数组元素,如下图:
关于重复元素的说明
TreeSet利用Comparable接口实现重复元素的判断,但是这样的操作只适合支持排序类集操作环境。而其他子类如果要消除重复元素,则必须依靠Object类中提供的两个方法。
- 取得哈希码:public int hashCode();
先判断对象的哈希码是否相同,依靠哈希码取得一个对象的内容。 - 对象比较:public boolean equals(Object obj);
再将对象的属性进行依次的比较。
例:利用HashSet子类保存自定义类对象
package Project.Study.SetClass;
import java.util.HashSet;
import java.util.Set;
class Student{
private String name;
private double score;
public Student(String name,double score){
this.name = name;
this.score = score;
}
@Override
public int hashCode(){
final int prime = 31;
int result = 1;
long temp;
temp = Double.doubleToLongBits(score);
result = prime*result+(int)(temp^(temp>>>32));
result = prime*result+((name==null)?0:name.hashCode());
return result;
}
@Override
public boolean equals(Object obj){
if (this==obj){
return true;
}
if (obj==null){
return false;
}
if (getClass()!=obj.getClass()){
return false;
}
Student other = (Student)obj;
if (Double.doubleToLongBits(score)!=Double.doubleToLongBits(other.score)){
return false;
}
if (name==null){
if (other.name!=null){
return false;
}
}else if (!name.equals(other.name)){
return false;
}
return true;
}
@Override
public String toString(){
return "学生姓名:"+this.name+",成绩:"+this.score+"\n";
}
}
public class Test4 {
public static void main(String[]args){
Set<Student>all = new HashSet<>(); //实例化Set接口
all.add(new Student("小明",88)); //保存数据
all.add(new Student("小黄",87.2));
all.add(new Student("小黄",87.5)); //部分重复
all.add(new Student("小黄",87.2)); //全部重复
all.add(new Student("小欢",88));
System.out.println(all);
}
}
//结果:
//[学生姓名:小欢,成绩:88.0
//, 学生姓名:小明,成绩:88.0
//, 学生姓名:小黄,成绩:87.2
//, 学生姓名:小黄,成绩:87.5
//]
TreeSet子类
如果希望保存的数据有序,那么可以使用Set接口的另一个子类:TreeSet子类。
例:使用TreeSet子类
package Project.Study.SetClass;
import java.util.Set;
import java.util.TreeSet;
public class Test2 {
public static void main(String[]args){
Set<String>all = new TreeSet<>(); //实例化Set接口
Set<Integer>num = new TreeSet<>();
all.add("b"); //保存数据
all.add("a");
all.add("c");
num.add(122);
num.add(12);
num.add(232);
System.out.println(all); //直接输出集合
System.out.println(num);
}
}
//结果:
//[a, b, c]
//[12, 122, 232]
由上程序我们可以看到,当使用TreeSet子类实例化了Set接口之后,所保存的数据将会变为有序数据,默认情况下按照升序排列。
关于数据排序的说明
TreeSet子类排序的实现依靠的是比较器接口(Comparable),即如果要利用TreeSet子类保存任意类的对象,那么该对象所在的类必须要实现java.lang.Comparable接口。
例:利用TreeSet保存自定义类对象
package Project.Study.SetClass;
import java.util.Set;
import java.util.TreeSet;
class Book implements Comparable<Book>{ //需要实现Comparable接口
private String title;
private double price;
public Book(String title,double price){
this.title=title;
this.price=price;
}
@Override
public String toString(){
return "书名:"+this.title+",价格:"+this.price+"\n";
}
@Override
public int compareTo(Book o){ //排序方法,比较所有属性
if (this.price>o.price){
return 1;
}else if (this.price<o.price){
return -1;
}else{
return this.title.compareTo(o.title);//调用String类的比较大小
}
}
}
public class Test3 {
public static void main(String[]args){
Set<Book>all = new TreeSet<>(); //实例化Set接口
all.add(new Book("Java",99)); //保存数据
all.add(new Book("C++",99)); //部分数据重复
all.add(new Book("Python",89.2));
System.out.println(all);
}
}
//结果:
//[书名:Python,价格:89.2
//, 书名:C++,价格:99.0
//, 书名:Java,价格:99.0
//]