java泛型与常用集合框架

一、泛型

Java泛型是jdk 1.5中引入的一个新特性。泛型是对Java原有的类型系统的一种扩展,其本质是参数化类型,把类型作为参数传递。

常见的泛型有泛型类、泛型接口、泛型方法

语法:<T, …> T称为类型占位符,表示一种引用类型

使用泛型的好处:

  1. 编译时检查类型安全,防止类型转换异常,提高代码的安全性
  2. 所有的强制转换都是自动的和隐式的,提高代码的重用性

1.1 泛型类的使用

/**
 * 泛型类
 * 语法:类名<T>
 * 描述:T是类型占位符,表示一种引用类型,可以写多个,用逗号隔开
 */
public class GenericClassDemo<T> {
	// 使用泛型创建变量
	public T t;
	// 注意:这里的t不能实例化。如:t = new T() 因为不确定T的构造函数
	
	// 泛型作为方法的参数
	public void setT(T t) {
		this.t = t;
	}
	
	// 泛型作为返回值
	public T getT() {
		return t;
	}
	
}

class Main {
	public static void main(String[] args) {
		// 占位符T只能接收引用数据类型,不能传递基本数据类型,要传递其对应的包装类
		GenericClassDemo<Integer> num = new GenericClassDemo<Integer>();
		num.setT(1);
		System.out.println(num.getT());
		
		GenericClassDemo<String> string = new GenericClassDemo<String>();
		string.setT("hehe");
		System.out.println(string.getT());
	}
}

1.2 泛型接口的使用

public interface MyInterface<T> {
	T show(T t);
}
// 实现类在实现泛型接口时必须指定类型,如果不指定接口类型,该实现类必须也得是泛型
public class MyInterfaceImpl implements MyInterface<String> {

	@Override
	public String show(String t) {
		return t;
	}

}

class Main02 {
	public static void main(String[] args) {
		MyInterfaceImpl impl = new MyInterfaceImpl();
		System.out.println(impl.show("haha"));
	}
}
// 如果不指定接口类型,该实现类必须也得是泛型
public class MyInterfaceImpl02<T> implements MyInterface<T> {

	@Override
	public T show(T t) {
		return t;
	}

}

class Main03 {
	public static void main(String[] args) {
		MyInterfaceImpl02<Integer> num = new MyInterfaceImpl02<Integer>();
		int n = num.show(100);
		System.out.println(n);
	}
}

1.3 泛型方法的使用

/**
 * 泛型方法
 * 语法:修饰符 <T> 类型 方法名() {}
 */
public class GenericMethodDemo {
	public String name;
	public <T> void show(T t) {
		System.out.println(t);
	}
}

class Main04 {
	public static void main(String[] args) {
		GenericMethodDemo demo = new GenericMethodDemo();
		demo.show("hehe");
		demo.show(123);
	}
}

二、集合

集合是一个对象的容器,定义了对对象进行操作的常用方法,类似于数组的功能。

集合与数组的区别:

  1. 数组长度固定,集合长度不固定
  2. 数组可以存储基本类型和引用类型,集合只能存储引用类型。(想要存储基本类型,需要使用基本类型对应的包装类)

集合包的导入:java.util.*

2.1 集合框架体系图

在这里插入图片描述

简化图:

在这里插入图片描述

2.2 Collection接口

Collection接口部分允许重复的对象(List),而部分不允许(Set),是层次接口的根接口,List和Set接口都实现了Collection接口。Collection接口按照索引值的方式存放数据,即存储的第一个数码索引为0,第二个为1,以此类推。

通常情况下,并不去创建实现这个接口的对象,而是将这个接口的引用去指向实现了其子接口的类的对象,比如ArrayList类的对象。

Collection<String> collection = new ArrayList<String>();

这里需要注意的一点是如果不使用泛型集合,那么遍历数据时只能指定为Object,但有时需要将Object强制转换为实际类型,还需对集合内的元素的类型进行判断,比较麻烦。所以,在创建集合时,最好是指定类型。这样做的好处是在获取元素的时候,类型会自动转换,这也是集合使用泛型的最直接的好处。集合使用泛型之后,可以达到元素类型明确的目的,避免了手动类型转换的过程,同时,也让我们更加明确容器保存的是什么类型的数据。

		Collection collection = new ArrayList();
		collection.add("hello");
		collection.add(123);
		for (Object obj : collection) {
			// 不使用泛型集合时,在转换类型时需要判断
			if (obj instanceof String) {
				obj = (String)obj;
			} else if (obj instanceof Integer) {
				obj = (int)obj;
			}
		}
2.2.1 Collection接口的方法
方法描述
boolean add(Object obj)添加一个对象
boolean addAll(Collection c)向集合中添加指定的一组对象
void clear()从此列表中删除所有元素
boolean contains(Object o)检查此集合中是否包含o对象
boolean containsAll(Collection<?> c)检查此集合中是否包含一组对象
boolean equals(Object o)比较此集合是否与指定对象相等
boolean isEmpty()判断此集合是否为空
boolean remove(Object o)在此集合中移出o对象
boolean removeAll(Collection<?> c)在此集合中移出一组对象
boolean retainAll(Collection<?> c)在此集合中查找有无指定的集合
int size()返回此集合中的元素的个数
public Iterator iterator()获取collection的一个迭代器,用户遍历集合
int hashCode()返回哈希值
2.2.2 equals方法判断的依据

根据元素所在类的equals()方法进行判断,如果存入集合中的元素是自定义的类对象,自定义类要重写equals()方法

如:有一个Animal类,我们往集合中添加Animal的两个实列,现在想从集合中删除一个,可以使用remove()方法进行删除。

		Collection<Animal> collection = new ArrayList<>();
        Animal dog = new Animal("小狗", 2);
        Animal cat = new Animal("小猫", 1);
        collection.add(dog);
        collection.add(cat);
        System.out.println(collection.toString());
        collection.remove(new Animal("小狗", 2));
        System.out.println(collection.toString());

输出结果如下,发现并没有删除我们想要删除的元素。因为在往remove()方法中传入new Animal(“小狗”, 2)时,程序又开辟了一份新的堆空间,会有一个新的地址,和dog不是同一个对象,所以无法删除

[Animal{name='小狗', age=2}, Animal{name='小猫', age=1}]
[Animal{name='小狗', age=2}, Animal{name='小猫', age=1}]

然后重写一下Animal的equals()方法

	@Override
    public boolean equals(Object obj) {
        // 如果传入的为null值,返回false
        if (obj == null) {
            return false;
        }
        // 先判断是否属于Animal类型,如果不是,就返回true
        if (obj instanceof Animal) {
            // 再判断传入的是否为本身,是本身返回true,代表可以删除
            if (this == obj) {
                return true;
            }
            Animal animal = (Animal)obj;
            // 判断传入的对象的姓名和年龄是否相等,相等返回true,代表可以删除
            if (this.name.equals(animal.getName()) && this.age == animal.getAge()) {
                return true;
            }
        }
        return false;
    }

此时发现成功将dog这个对象从集合中移除

[Animal{name='小狗', age=2}, Animal{name='小猫', age=1}]
[Animal{name='小猫', age=1}]
2.2.3 Collection的遍历

因为Collection没有下标,所以不能使用普通的for循环来遍历,可以使用增加for循环或者迭代器对象来遍历。另外,如果想要通过迭代器遍历来移除Collection中的元素,不能使用Collection的remove()方法,因为会抛出一个并发修改异常,可以使用迭代器的remove()方法

Iterator有下面三个方法:

boolean hasNext()  // 判断集合中是否还有元素,有则返回true
E next()  // 返回迭代的下一个元素
void remove()  // 将迭代器新返回的元素删除
		Iterator<Animal> it = collection.iterator();
        while (it.hasNext()) {
            Animal animal = it.next();
            System.out.println(animal);
            it.remove();
        }
        System.out.println(collection.size());
Animal{name='小狗', age=2}
Animal{name='小猫', age=1}
0

2.3 List接口

Collection的一个子接口,实现并扩展了Colletion接口,元素有序、有下标、可重复,但不能有null值,该接口提供了get()和set()方法,用户可以根据元素的整数索引访问元素,并搜索列表中的元素,因此我们在遍历时可以使用普通for循环来遍历。

2.3.1 List接口的方法

List相对于Collection扩展了很多的方法,比如可以通过下标来添加、删除、修改、获取元素。而且List还增加了一个返回列表迭代器的方法,可用通过列表迭代器进行元素的遍历。

方法描述
void add(int index,Object o)将指定元素插入此列表中的指定位置
Object get(int index)返回此列表中指定位置的元素
int indexOf(Object o)返回指定元素在此列表中首次出现的索引;如果此列表不包含该元素,则返回-1
int lastIndexOf(Object o)返回指定元素在此列表中最后一次出现的索引;如果此列表不包含该元素,则返回-1
ListIterator listIterator()返回此列表中元素的列表迭代器
ListIterator listIterator(int index)从列表中的指定位置开始,以适当的顺序返回列表中元素的列表迭代器
Object remove(int index)删除此列表中指定位置的元素
Object remove(Object o)删除此列表中第一次出现的指定对象
Object set(int index, Object o)用指定元素替换此列表中指定位置的元素

测试使用列表迭代器遍历List

		List<String> list = new ArrayList<String>();
		list.add("xxx");
		list.add("yyy");
		list.add("zzz");
		// 使用列表迭代器遍历
		System.out.println("使用列表迭代器遍历结果如下:");
		ListIterator<String> listIt = list.listIterator();
		System.out.println("从前往后:");
		while(listIt.hasNext()) {
			System.out.println(listIt.nextIndex() + ": " + listIt.next());
		}
		System.out.println("从后往前:");
		// 从后往前需要将迭代器的指针移到尾部,因为刚刚遍历时已经将指针移到尾部,所以这里可以直接调用previous()方法
		while(listIt.hasPrevious()) {
			System.out.println(listIt.previousIndex() + ": " + listIt.previous());
		}

运行结果:

使用列表迭代器遍历结果如下:
从前往后:
0: xxx
1: yyy
2: zzz
从后往前:
2: zzz
1: yyy
0: xxx
2.3.2 List接口的实现类

List接口的实现类大多以List作为命名的后缀,常用的有ArrayList与LinkedList。

1. ArrayList

ArrayList的底层基于数组实现容量大小动态变化,查询快、增删慢,JDK1.2版本,运行效率快、线程不安全,允许null 的存在。

2. LinkedList

链表结构实现,增删快、查询慢

ArrayList和LinkedList的区别

  • ArrayList:必须开辟连续的空间,查询比较快,但增删比较慢
  • LinkedList:无序开辟连续的空间,查询比较慢,但增删比较快

**根据ArrayList源码分析扩容原理:

// 默认容量大小10
private static final int DEFAULT_CAPACITY = 10;

// 用于空实例的共享空数组实例
private static final Object[] EMPTY_ELEMENTDATA = {};

// 与EMPTY_ELEMENTDATA区别开来,以了解添加第一个元素时需要扩充多少
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

/*存储ArrayList的元素的数组缓冲区。ArrayList的容量是此数组缓冲区的长度。任何具有
elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA的空ArrayList添加第一个元素时,
将扩展为DEFAULT_CAPACITY。也就是说如果没有向集合中添加任何元素时,容量为0,添加一个元素之后容量为10*/
transient Object[] elementData;

/*ArrayList的大小*/
private int size;

使用空参构造创建一个ArrayList对象后,elementData(以后存放数据的数组)会被初始化为DEFAULTCAPACITY_EMPTY_ELEMENTDATA,即一个空的数组。此时ArrayList实例的大小仍为0。

/*构造一个初始容量为10的空列表*/
public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

我们先拿刚创建出来的空的ArrayList的一个实例来说。在第一次调用add()方法时,内部会调用ensureCapacityInternal(int minCapacity)方法来判断elementData(我们实际存放数据的数组)是否为空数组DEFAULTCAPACITY_EMPTY_ELEMENTDATA(刚创建出来的)。如果是,就拿DEFAULT_CAPACITY的值10来和minCapacity(第一次传入的size为0,以后依次累加)进行比较,去最大的值赋给minCapacity。因为我们是第一次调用add()方法,ArrayList对象也是刚创建出来的,所以这里minCapacity的值为10。

public boolean add(E e) {
        ensureCapacityInternal(size + 1);
        elementData[size++] = e;
        return true;
}

private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
}

然后在内部调用ensureExplicitCapacity(int minCapacity)方法进行判断是否需要扩容,这里10 - 0 > 0,

private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
}

所以继续调用grow(int minCapacity)方法进行扩容。oldCapacity的值赋为0,0右移以为仍为0,因此newCapacity也为0,然后newCapacity - minCapacity = -10 < 0,因此newCapacity = 10,然后调用Arrays.copyOf()进行数组的扩容。

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

private void grow(int minCapacity) {
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        elementData = Arrays.copyOf(elementData, newCapacity);
}

然后在第二次调用add()方法时,内部再次调用ensureCapacityInternal(int minCapacity),但此时数组不为空,已经不满足elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA,所以minCapacity等于传入的size的值为1,然后继续调用ensureExplicitCapacity(int minCapacity)进行判断是否需要扩容,此时minCapacity - elementData.length = 1 - 10 < 0不满足条件,所以不再调用grow(int minCapacity)方法进行扩容,直接执行elementData[size++] = e。

如果现在集合中已经有10个元素了,我们又调用了一次add(e)方法,在调用ensureExplicitCapacity(int minCapacity)方法进行判断时,11 - 10 > 0,此时满足扩容条件,需要调用grow(int minCapacity)进行扩容。然后在grow(int minCapacity)方法内,oldCapacity = 10,然后右移一位为5,此时newCapacity = oldCapacity + (oldCapacity >> 1) = 10 + 5 = 15,然后在调用Arrays.copyOf()方法进行扩容,容量扩容到了原来的1.5倍。

2.4 Set接口

Set接口实现并扩展了Collection接口,无序、无下标、不允许存在重复项(包括唯一null值)。每个具体的Set实现类依赖equals()方法来检查唯一性。Set中也没有get()方法,遍历时也需依靠Iterator或者使用增强for循环,需要注意的是遍历时顺序和在Set中存储的顺序可能不同。

2.2.1 Set接口的方法

Set接口中没有引入新的方法,所以Set就是一个Collection,只是重写了原有的方法。这里只需注意add()方法和addAll()方法,其它方法和List的方法的使用基本一致。

方法描述
void boolean add(Object obj)如果Set中尚未存在指定的元素,则添加;如果存在,不作处理
void boolean addAll(Object obj)向当前Set中添加在obj中有但Set中没有的元素
2.4.2 Set接口的实现类

Set接口的实现类大多以Set为后缀,其中HashSet和TreeSet最为常用。

1. HashSet
  • 基于HashCode计算元素存放位置,不能保证有顺无序、无下标、元素不重复

  • 存储结构:哈希表(数组+链表+红黑树(JDK1.8之后))

  • 存储过程:根据hashCode计算保存的位置,如果位置为空,则直接保存,如果不为空再执行equals()方法,如果equals为true,则认为重复,否则形成链表

    在向HashSet中添加自定义对象时,如果使用remove()方法删除元素时,会发现一个跟上面Collection删除元素时发生的问题,无法删除的情况,根据HashSet的存储过程可知,我们可以重写hashCode()和equals()方法,当然我们也可以直接使用编译器自动生成。

		HashSet<Animal> hashSet = new HashSet<Animal>();
		Animal dog = new Animal("小狗", 2);
		Animal cat = new Animal("小猫", 1);
		Animal chick = new Animal("小鸡", 1);
		Animal duck = new Animal("小鸭", 2);
		System.out.println(hashSet.toString());
		hashSet.remove(new Animal("小猫", 1));
		System.out.println(hashSet.toString());

重写hashCode()和equals()方法之前的运行结果如下,并没有删除小猫这个对象,同时我们也可以发现存放的顺序和我们添加时的顺序并不一致

[Animal [name=小鸡, age=1], Animal [name=小狗, age=2], Animal [name=小猫, age=1], Animal [name=小鸭, age=2]]
[Animal [name=小鸡, age=1], Animal [name=小狗, age=2], Animal [name=小猫, age=1], Animal [name=小鸭, age=2]]

重写hashCode()和equals()方法

	@Override
	public int hashCode() {
		int n1 = this.name.hashCode();
		int n2 = this.age;
		return n1 + n2;
	}
	
	@Override
	public boolean equals(Object obj) {	
		if (obj == null) {
			return false;
		}
		// 判断是否属于Animal类型
		if (obj instanceof Animal) {
			// 如果传入的是本身,返回true,代表可以删除
			if (this == obj) {
				return true;
			}
			Animal animal = (Animal)obj;
			if (this.name.equals(animal.getName()) && this.age == animal.getAge()) {
				return true;
			}
		}
		return false;
	}

重写hashCode()和equals()方法后运行结果如下,得到了我们想要的结果

[Animal [name=小狗, age=2], Animal [name=小猫, age=1], Animal [name=小鸡, age=1], Animal [name=小鸭, age=2]]
[Animal [name=小狗, age=2], Animal [name=小鸡, age=1], Animal [name=小鸭, age=2]]
2. TreeSet
  • 实现了SortedSet接口,对集合元素自动排序,且元素不重复
  • 存储结构:红黑树
  • 元素存储为自定义类型时必须实现Comparable接口,重写CompareTo方法,指定排序规则

① 当存储的元素为内置数据类型或String对象时,TreeSet会自动对数据排序,例如:

		TreeSet<Integer> treeSet = new TreeSet<Integer>();
		treeSet.add(2);
		treeSet.add(5);
		treeSet.add(9);
		treeSet.add(1);
		treeSet.add(3);	
		System.out.println(treeSet.toString());

输出结果:

[1, 2, 3, 5, 9]

需要注意的是:String类已经帮我们重写了compareTo()方法,所以当存储String类对象时,TreeSet也会自动对数据排序。

② 当存储的元素为自定义数据类型时,在调用add()方法时会报错,这时需要我们实现Comparable接口的compareTo方法,自定义我们的排序规则。以Animal类为例:

public class Animal implements Comparable<Animal> {
	private String name;
	private int age;
	public Animal(String name, int age) {
		this.name = name;
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	@Override
	public String toString() {
		return "Animal [name=" + name + ", age=" + age + "]";
	}
	@Override
	public int compareTo(Animal o) {
		int n1 = this.getName().compareTo(o.getName());
		int n2 = this.age - o.getAge();
		return n1==0 ? n2 : n1;  // 以name属性为主进行排序,如果name相同,再根据age属性进行排序
	}
}
		TreeSet<Animal> treeSet = new TreeSet<Animal>();
		Animal dog = new Animal("dog", 2);
		Animal cat = new Animal("cat", 1);
		Animal chick = new Animal("chick", 1);
		Animal duck = new Animal("duck", 2);
		
		treeSet.add(dog);
		treeSet.add(cat);
		treeSet.add(chick);
		treeSet.add(duck);
		treeSet.add(duck);
		
		System.out.println(treeSet.toString());

运行结果:

[Animal [name=cat, age=1], Animal [name=chick, age=1], Animal [name=dog, age=2], Animal [name=duck, age=2]]

当然,在使用remove()方法时,仍需要重写hashCode()和equals()方法。

③通过Comparator比较器指定排序规则

		TreeSet<Animal> treeSet = new TreeSet<Animal>(new Comparator<Animal>() {
		
			@Override
			public int compare(Animal o1, Animal o2) {
				int n1 = o1.getAge() - o2.getAge();
				int n2 = o1.getName().compareTo(o2.getName());
				
				return n1 == 0 ? n2 : n1;  // 以age属性为主进行排序
			}
		});
		Animal dog = new Animal("dog", 2);
		Animal cat = new Animal("cat", 1);
		Animal chick = new Animal("chick", 1);
		Animal duck = new Animal("duck", 2);
		
		treeSet.add(dog);
		treeSet.add(cat);
		treeSet.add(chick);
		treeSet.add(duck);
		System.out.println(treeSet.toString());

2.5 Map接口

Map接口采用了一种与上面三种接口完全不同的方式来存储数据,在这种存储方式中,为每个存储的数据设定一个名称(任意非null对象都可以作为名称),以后按照该名称操作数据,要求名称不能重复,每个名称对应唯一的一个值。这种存储数据的方式也称做名称-数值对,常称为键值对存储。

Map的特点:

  • 用于存储任意键值对(key-value)
  • 键:无序、无下标、不允许重复
  • 值:无序、无下标、允许重复
2.5.1 Map接口常用的方法
方法描述
V put(K key, V value)将指定值value与Map中的指定键key相关联
void putAll(Map m)将指定Map中的所有数据按照原来的格式复制到当前Map中
V remove(Object key)如果存在,则从Map中删除键为key的值
void clear()清空当前Map
boolean containsKey(Object key)如果Map包含指定键key,则返回true
boolean containsValue(Object value)如果Map包含指定的值value,则返回true
V get(Object key)获取Map中键为key对应的值
boolean isEmpty()判断当前Map是否为空
int size()获取当前Map中键值对的个数
Set<Map.Entry<K,V>> entrySet()返回当前Map中所有的键值对,以Set的形式返回,Set中的元素类型为Map.Entry
Set keySet()返回当前Map中所有的名称,将所有的名称以Set的形式返回
Collection values()返回当前Map所有值的Collection集合
2.5.2 Map的遍历
  1. 使用keySet()方法进行遍历,这个方法会返回当前Map中所有的名称,将所有的名称以Set的形式返回,然后再去遍历Set。例如:
		Map<String, String> map = new HashMap<String, String>();
		map.put("name", "tom");
		map.put("age", "16");
		map.put("gender", "male");
		Set<String> keySet = map.keySet();
		for (String key : keySet) {
			System.out.println(key + ": " + map.get(key));
		}
  1. 使用entry()方法进行遍历,返回当前Map中所有的键值对,以Set的形式返回,Set中存放着一个个的Entry对象,其中的每一个Entry对象又包含着键值对,可通过Entry的getKey()和getValue()方法分别来获取键和值。例如:
		Set<Map.Entry<String, String>> entries = map.entrySet();
		for (Entry<String, String> entry : entries) {
			System.out.println(entry);
			System.out.println(entry.getKey() + ":" + entry.getValue());
		}
2.5.3 Map接口的实现类

Map有很多的实现类,大多以Map为后缀,其中HashMap和TreeMap最为常用。

1. HashMap

HashMap类实现了接口Map,使用散列表存储数据,不对数据进行排序,并且允许null键和null值的存在。HashMap效率要好于TreeMap,适用于在Map中插入、删除、定位。

有一点需要指出,我们可以向HashSet那样去重写加入HashMap中的对象的hashCode()和equals()方法,已达到我们想要的结果。比如,调用remove()删除元素,示例如下:

		HashMap<Student, String> students = new HashMap<Student, String>();
		Student s1 = new Student(1, "tom");
		Student s2 = new Student(2, "jerry");
		
		students.put(s1, "北京");
		students.put(s2, "上海");
		students.remove(new Student(2, "jerry"));
		System.out.println(students.toString());

重写Student类的hashCode()和equals()方法前,运行结果显示并没有将s2删除。

{Student [num=2, name=jerry]=上海, Student [num=1, name=tom]=北京}

重写Student类的hashCode()和equals()方法

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		result = prime * result + num;
		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 (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		if (num != other.num)
			return false;
		return true;
	}

运行结果显示成功删除

{Student [num=1, name=tom]=北京}
2. TreeMap

TreeMap类实现了Map接口及其子接口SortedMap,实现该类的Map集合不允许键对象为null。同时,该类实现的树基于红黑树,总是处于平衡状态。该类适用于按自然顺序或自定义顺序遍历键值,并且一般情况下,TreeMap的效率低于HashMap。

因为TreeMap是有序的,所以当指定的泛型类型为自定义数据类型时,需要像TreeSet那样,让自定义类实现Comparable接口的compareTo()方法,来自定义排序规则。

例如:按照学生类学号进行排序,来重写compareTo()方法

	@Override
	public int compareTo(Student stu) {
		int n2 = this.num - stu.getNum();
		return n2;
	}

实例程序:

		TreeMap<Student, String> treeMap = new TreeMap<Student, String>();		
		Student s1 = new Student(1, "tom");
		Student s2 = new Student(2, "jerry");		
		Student s3 = new Student(3, "Anni");
		Student s4 = new Student(4, "Lucy");
		
		treeMap.put(s2, "上海");
		treeMap.put(s4, "上海");
		treeMap.put(s3, "上海");
		treeMap.put(s1, "北京");
		System.out.println(treeMap.toString());

运行结果:

{Student [num=1, name=tom]=北京, Student [num=2, name=jerry]=上海, Student [num=3, name=Anni]=上海, Student [num=4, name=Lucy]=上海}

2.6 总结

存放顺序有无下标
ArrayList按存储时顺序存放可以重复,可以有多个null
LinkedList按存储时顺序存放可以重复,可以有多个null
HashSet基于HashCode计算存放位置不可重复,可为null,但只能有一个
TreeSet自动排序或自定义排序不可重复,不可为null
HashMap基于HashCode计算存放位置不可重复,可以为null可重复 可以为null
TreeMap自动排序或自定义排序不可重复,不可为null可重复 可以为null

三、Collections操作集合

3.1 Collections与collection的区别

  1. java.util.Collections是一个工具类。它包含有各种有关集合操作的静态多态方法。此类不能实例化,服务于Java的Collection框架。他提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作。
  2. java.util.Collection 是一个 集合框架的父接口。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式。

3.2 Collections的基本使用

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * Collections工具类的使用
 */
public class CollectionsDemo {
	public static void main(String[] args) {
		List<Integer> list = new ArrayList<Integer>();
		list.add(1);
		list.add(6);
		list.add(5);
		list.add(2);
		list.add(8);
		System.out.println("排序前:" + list.toString());
		
		// 1. sort排序
		Collections.sort(list);
		System.out.println("排序后:" + list.toString());
		
		// 2. 二分查找 返回下标
		int num = Collections.binarySearch(list, 8);
		System.out.println("查找结果(索引):" + num);
		
		// 3.拷贝
		List<Integer> dest = new ArrayList<Integer>();
		for (int i = 0; i < list.size(); i++) {
			dest.add(0);
		}
		Collections.copy(dest, list);
		System.out.println("目的数组:" + dest);
		
		// 4.反转
		Collections.reverse(list);
		System.out.println("反转后:" + list);
		
		// 5.乱序
		Collections.shuffle(list);
		System.out.println("乱序后:" + list);
				
		// 6.list转数组
		Integer[] arr = list.toArray(new Integer[0]);
		System.out.println("集合转数组:" + Arrays.toString(arr));
		
		// 7.数组转集合,基本数据类型要使用其对应的包装类
		List<Integer> list2 = Arrays.asList(arr);
		// list2.add(10);  // 注:转换后的集合为受限集合,不能添加和删除
		System.out.println("数组转集合:" + list2.toString());		
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值