从零开始学JAVA——集合(一)

一、集合框架的概述

1.集合、数组都是对多个数据进行存储操作的结构,简称JAVA容器。
说明:此时的存储,主要指的时内存层面的存储,不涉及到持久化的存储(.txt.jpg.avi)
2.1数组在存储多个数据方面的特点:
1)一旦初始化以后,其长度就确定了。
2)数组一旦定义好,其元素的类型也就确定了。我们也只能操作指定类型的数据了。
比如:String[] arr;int[] arr1;Object[] arr2;
2.2数组在存储多个数据方面的缺点:
1)一旦初始化以后,其长度就不可修改。
2)数组中提供的方法非常有限,对于添加、删除、插入数据等操作,非常不便,同时效率不高。
3)获取数组照片那个的实际元素的个数的需求,数组没有现成的属性或方法可以用。
4)数组存储数据的特点:有序、可重复。对于无序、不可重复的需求,不能满足。

二、集合框架

Collection接口:单例集合,用来存储一个一个的对象
下面有List接口:存储有序的、可重复的数据 -->动态数组
实现类:ArrayList、LinkedList、Vector
和Set接口:存储无序的,不可重复的数据 -->高中讲的集合
实现类:HashSet、LinkedHashSet、TreeSet

Map接口:双列集合,用来存储一对(key-value)一对的数据 -->高中函数
实现类:HashMap、LinkedHashMap、TreeMap、Hashtable、Properties

public class CollectionTest {
	@Test
	public void test1() {
		Collection coll= new ArrayList();
		
		//add(Object e);将元素e添加到集合coll中
		coll.add("AA");
		coll.add("BB");
		coll.add("CC");
		coll.add("DD");
		coll.add("EE");
		
		System.out.println(coll.size());
		
		//addAll(coll);将coll集合中的元素添加到当前集合中
		Collection coll2= new ArrayList();
		coll2.add(123);
		coll2.add(456);
		coll2.addAll(coll);
		//System.out.println(colls.size());
		System.out.println(coll2.size());
		System.out.println(coll2);
		
		//clear()清空集合元素
		coll2.clear();
		//isEmpty():判断当前集合是否为空
		System.out.println(coll2.isEmpty());
	}
}

三、 Colletion接口声明的方法的测试

向collection接口的实现类的对象中添加数据obj时,要求obj所在类要重写equals()

public class CollectionTest {
	@Test
	public void test1() {
		Collection coll = new ArrayList();
		coll.add(123);
		coll.add(434);
		coll.add(new String("Tom"));
		Person p = new Person("Tom2",22);
		coll.add(p);
		
		coll.add(new Person("Ton3",20));
		
		//1.contains(Object obj)判断当前集合是否包含obj
		//我们会在判断时会调用obj对象所在类的equals()
		boolean contains = coll.contains(123);
		System.out.println(contains);
		System.out.println(coll.contains(new String("Tom")));
		System.out.println(coll.contains(p));
		System.out.println(coll.contains(new Person("Ton3",20)));
		
		//2.containsAll(Collection coll);
		Collection coll1 = Arrays.asList(123,456);
		System.out.println(coll.containsAll(coll1));
	}
			
	@Test
	public void test2() {
		//3.remove(Object obj)从当前集合中移除obj元素
		Collection coll = new ArrayList();
		coll.add(123);
		coll.add(434);
		coll.add(new String("Tom"));
		Person p = new Person("Tom2",22);
		coll.add(p);
		coll.add(new Person("Ton3",20));
		
		coll.remove(123);
		System.out.println(coll);
		//4.removeAll(Collection coll2);差集:从当前集合移除coll2中所有元素
		Collection coll2 = Arrays.asList(123,467);
		coll.removeAll(coll2);
		System.out.println(coll);
		
	}
	
	@Test
	public void test3() {
		Collection coll = new ArrayList();
		coll.add(123);
		coll.add(434);
		coll.add(new String("Tom"));
		
		//5.retainAll(Collection coll2):交集:获取当前集合和coll2集合的交集,并返回给当前集合
//		Collection coll2 = Arrays.asList(123,467);
//		coll.retainAll(coll2);
//		System.out.println(coll);
		
		//6.equals(Object obj):要想返回是true,需要当前集合和形参集合的元素都相同
		Collection coll3 = new ArrayList();
		coll3.add(123);
		coll3.add(434);
		coll3.add(new String("Tom"));
		
		System.out.println(coll.equals(coll3));
		
	}
	
	@Test
	public void test4() {
		Collection coll = new ArrayList();
		coll.add(123);
		coll.add(434);
		coll.add(new String("Tom"));
		
		//7.hashCode():返回当前对象的哈希值
		System.out.println(coll.hashCode());
		
		//8.集合到数组:toArray();
		Object[] arr = coll.toArray();
		
		for (int i = 0; i < arr.length; i++) {
			System.out.println(arr[i]);
		}
		
		//把数组转换为集合:d调用Arrays类的静态方法asLIst();
		List<String> list = Arrays.asList(new String[] {"AA","BB","CC"});
		System.out.println(list);
		
//		List arr1 = Arrays.asList(new int[] {123,456});
		List arr1 = Arrays.asList(123,456);
		System.out.println(arr1.size());
		
		List arr2 = Arrays.asList(new Integer[] {123,456});
		System.out.println(arr2.size());
		
		//9.iterator():返回Iterator接口实例,用于遍历集合元素,放在IteratorTest.java中调试
	}
}

Person类

public class Person {
	private String name;
	private int age;
	public Person() {
		
	}
	public Person(String name, int age) {
		super();
		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 "Person [name=" + name + ", age=" + age + "]";
	}
//	@Override
//	public int hashCode() {
//		final int prime = 31;
//		int result = 1;
//		result = prime * result + age;
//		result = prime * result + ((name == null) ? 0 : name.hashCode());
//		return result;
//	}
	@Override
	public boolean equals(Object obj) {
		System.out.println("Person equals()");
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Person other = (Person) obj;
		if (age != other.age)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}
	
}

四、集合元素遍历操作,使用迭代器Iterator接口

1.内部方法hasNext()和next();
2.集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合的第一个元素之前
3.内部定义remove(),可以在遍历的时候,删除集合中的元素。此方法不同于集合直接调用remove()

public class IterarorTest {
	@Test 
	public void test1() {
		Collection coll = new ArrayList();
		coll.add(123);
		coll.add(434);
		coll.add(new String("Tom"));
		
		Iterator iterator = coll.iterator();
//		方式一
//		System.out.println(iterator.next());
//		System.out.println(iterator.next());
//		System.out.println(iterator.next());
//		//NoSuchElementException
//		System.out.println(iterator.next());
		
//		方式二:不推荐
//		for (int i = 0; i < coll.size(); i++) {
//			System.out.println(iterator.next());
//		}
		
		//方式三
		while(iterator.hasNext()) {
			System.out.println(iterator.next());
		}
	}
	
	@Test
	public void test2() {
		Collection coll = new ArrayList();
		coll.add(123);
		coll.add(434);
		coll.add(new String("Tom"));
		
		Iterator iterator = coll.iterator();
		//错误方式一
//		while(iterator.next()!=null) {
//			System.out.println(iterator.next());
//		}
		
		//错误方式二
		//集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合的第一个元素之前
//		while(coll.iterator().hasNext()) {
//			System.out.println(coll.iterator().next());
//		}
	}
	
	//测试Iterator中的remove()
	//如果调用next()或在上一次调用next方法之后已经调用了remove方法
	//再调用remove都会报IllegalSateException
	@Test
	public void test3() {
		Collection coll = new ArrayList();
		coll.add(123);
		coll.add(434);
		coll.add(new String("Tom"));
		coll.add(new Person("DT",12));
		
		Iterator iterator = coll.iterator();
		//删除集合中"Tom"
		while(iterator.hasNext()) {
			Object obj = iterator.next();
			if("Tom".equals(obj)) {
				iterator.remove();
			}
		}
		iterator = coll.iterator();
		while(iterator.hasNext()) {
			System.out.println(iterator.next());
		}
	}
}

JDK5.0新增了foreach循环,用于遍历集合和数组

public class ForTest {

	@Test
	public void test1() {
		Collection coll = new ArrayList();
		coll.add(123);
		coll.add(434);
		coll.add(new String("Tom"));
		coll.add(new Person("DT",12));
		
		//for(集合元素的类型 局部变量 : 集合对象)
		//内部仍然使用了迭代器
		for(Object obj : coll) {
			System.out.println(obj);
		}
	}
	
	@Test 
	public void test2() {
		int[] arr = new int[] {1,2,3,4,5};
		//for(数组元素的类型 局部变量 : 数组对象)
		for(int i : arr) {
			System.out.println(i);
		}
	}
	@Test
	public void test3() {
		String[] arr = new String[]{"MM","MM","MM"};
		
		//方式一:普通for赋值
//		for(int i = 0;i<arr.length;i++) {
//			arr[i] = "GG";
//		}
		
		//方式二:增强for循环
		for(String s : arr) {
			s="GG";
		}
		
		for (int i = 0; i < arr.length; i++) {
			System.out.println(arr[i]);
		}
	}

}

五、list接口框架

List接口:

存储有序的、可重复的数据 -->动态数组,替换原有的数组
实现类:
ArrayList:作为List接口的主要实现类,线程不安全,效率高
LinkedList:对于频繁的插入、删除操作,使用此类效率比ArrrayList高;底层使用双向链表存储
Vector:作为List接口古老实现类,线程安全的,效率低;底层使用object[]elementData存储

2.ArrayList的源码分析:

2.1jdk7的情况下
ArrayList list = new ArrayList();//底层创建了长度是10的object[]数组elementData
list.add(123)//elementData[0] = new Integer(123);

list.add(11);如果此次的添加导致底层elementData数组容量不够,则扩容,默认情况下,扩容为原来的容量的1.5倍,同时需要将原来数组中的数组复制到新的数组中
结论:建议开发中使用带参的构造器:ArrayList list = new ArrayList(int capacity)

2.2jdk8中ArrayList的变化:
ArrayList list = new ArrayList();//底层Objcet[]elementData初始化为{}并没有创建长度为10的数组
list.add(123);//第一次调用add()时,底层才创建长度为10的数组,并将123添加到elementData[0]

后续的添加和扩容与JDK7一样

2.3小结:JDK7中的ArrayList的创建类类似于单例的饿汉式,而JDK8中的ArrayList的对象的创建
类似于单例的懒汉式,延迟了数组的创建,节省内存。

3.LinkedList的源码分析

LinkedList list = new LinkedList();内部声明了Node类型的first和last属性,默认值为null
list.add(123);将123封装到Node中,创建了Node对象,
Node定义为:体现了LinkedList的双向链表的说法
private static class Node {
E item;
Node next;
Node prev;

    Node(Node<E> prev, E element, Node<E> next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
    }
}

4.Vector的源码分析

JDK7和JDK8中通过Vector()构造器创建对象时,底层都创建了长度为10的数组,
在扩容方面,默认扩容为原来数组长度的两倍

总结:常用方法
增:add(Object obj)
删:remove(int index)/remove(Object obj)
改:set(int index, Object ele)
查:get(int index)
插:add(int index, Object ele)
长度:size()
遍历:①Iterator迭代器方式。②增强for循环。③普通循环
*/

public class ListTest {
	private List<Integer> asList;

	@Test
	public void test1() {
		ArrayList  list = new ArrayList();
		list.add(123);
		list.add(456);
		list.add("AAA");
		list.add(new Person("Tom",16));
		list.add(123);
		
		System.out.println(list);
		//add()添加元素
		list.add(1,"XX");
		System.out.println(list);
		//addAll()添加此集合的所有元素
		List list1 = Arrays.asList(1,2,3);
		list.addAll(list1);
		System.out.println(list.size());
		//Object get(int index):获取指定index位置的元素
		System.out.println(list.get(0));
		
		//int indexOf(Object obj):返回obj在集合中首次出现的位置。如果不存在返回-1
		int indexOf = list.indexOf(456);
		System.out.println(indexOf);
		//int lastIndexOf(Object obj):返回在当前集合中末次出现的位置。如果不存在返回-1
		System.out.println(list.lastIndexOf(123));
		//Object remove(int index):移除指定index位置的元素,并返回此元素
		Object obj = list.remove(0);
		System.out.println(obj);
		System.out.println(list);
		
		//Object set(int index,Object ele):设置指定index位置的元素为ele
		list.set(1, "KK");
		System.out.println(list);
		
		//List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的左闭右开区间的子集
		
		List subList = list.subList(2, 5);
		System.out.println(subList);
		System.out.println(list);
		
		//方式一:Iterator迭代器方式
		System.out.println("方式一:");
		Iterator iterator = list.iterator();
		while(iterator.hasNext()) {
			System.out.println(iterator.next());
		}
		
		System.out.println("方式二:");
		for(Object obj1 : list) {
			System.out.println(obj1);
		}
		
		System.out.println("方式三:");
		for(int i = 0; i<list.size();i++) {
			System.out.println(list.get(i));
		}

	}
}

/*

Set接口

存储无序的,不可重复的数据 -->高中讲的集合
实现类:
HashSet:作为set接口的主要实现类;线程不安全的;可以存储null值
LinkedHashSet:作为HashSet的子类;遍历其内部数据时,可以按照添加的顺序遍历
TreeSet:可以按照添加对象的指定属性,进行排序。

1.Set接口中没有额外定义新的方法,使用的都是Collection中声明过的方法。
2.要求:向Set中添加的数据,其所在的类一定要重写hashCode()和equals()
要求:重写的hashCode()和equals()尽可能保持一致性:相等的对象必须具有相等的散列码
重写两个方法的小技巧:对象中用作equals()方法比较Field,都应该用来计算hashCode()
1.set:存储无序的、不可重复的数据

以HashSet为例说明
1).无序性:不等于随机性。存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值
2).不可重复性:保证添加的元素按照equals()判断时,不能返回true。即:相同的元素只能添加一个。

2.添加元素的过程,以HashSet为例:

我们向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算机元素a的哈希值
此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置(集为:索引位置),判断数组位置
上是否已经有元素:
如果此位置上没有其他元素,则元素a添加成功。—>情况1
如果此位置上有其他元素b,(或以链表形式存在的多个元素),则比较元素a与元素b的hash值
如果hash值不相同,则元素a添加成功—>情况2
如果hash值相同,进而需要调用元素a所在类的equals()方法
equals()返回true,元素a添加失败
equals()返回false,则元素a添加成功—>情况3
对于添加成功的情况2和情况3而言:元素a与已经存在指定索引位置上数据以链表的方式存储。
JDK7:元素a放到数组中,指向原来的元素
JDK8:原来的元素在数组中,指向元素a
总结:七上八下
HashSet底层:数组+链表的结构

在这里插入图片描述

public class SetTest {
	@Test
	public void test1() {
		Set set = new HashSet();
		set.add(456);
		set.add(123);
		set.add("AA");
		set.add("CC");
		set.add(new String("Tom"));
		set.add(new User("Tom",13));
		set.add(new User("Tom",13));
		set.add(new User("Tom",13));
		set.add(129);
		
		Iterator iterator = set.iterator();
		while(iterator.hasNext()) {
			System.out.println(iterator.next());
		}
	}
	
	//LinkedHashSet的使用
	//LinkedHashSet作为HashSet的子类,在添加数据的同时,每个数据还维护了两个引用,记录此数据前一个
	//数据和后一个数据
	//有点:对于频繁的遍历操作,LinkedHashSet效率高于HashSet
	
	@Test
	public void test2() {
		Set set = new LinkedHashSet();
		set.add(456);
		set.add(123);
		set.add("AA");
		set.add("CC");
		set.add(new String("Tom"));
		set.add(new User("Tom",13));
		set.add(new User("Tom",13));
		set.add(new User("Tom",13));
		set.add(129);
		
		Iterator iterator = set.iterator();
		while(iterator.hasNext()) {
			System.out.println(iterator.next());
		}
	}
}

TreeSet

1.向TreeSet中添加的数据,要求是相同的对象
2.两种排序方式:自然排序(实现Comparable接口)和定制排序(Comparator)
3.自然排序中,比较两个对象是否相同的标准为:compareTo()返回0,不再是equals()
4.制定排序中:比较两个对象是否相同的标准为:compare()返回0,不再是equals()。

在这里插入图片描述


import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;

import org.junit.Test;

public class TreeTest {	
	@Test
	public void test1() {
		TreeSet set = new TreeSet();
//		失败:不能添加不同类的对象
//		set.add(123);
//		set.add(456);
//		set.add("AA");
//		set.add(new User("Tom",12));
		
//		举例一:
//		set.add(24);
//		set.add(12);
//		set.add(-12);
				
//		举例二
		set.add(new User("Tom",12));
		set.add(new User("jerry",22));
		set.add(new User("jim",31));
		set.add(new User("jack",45));
		set.add(new User("jack",66));
		Iterator iterator = set.iterator();
		while(iterator.hasNext()) {
			System.out.println(iterator.next());
		}
	}
	
	@Test
	public void test2() {
		Comparator com = new Comparator() {
			//按照年龄从小到大排序
			@Override
			public int compare(Object o1, Object o2) {
				// TODO Auto-generated method stub
				if(o1 instanceof User && o2 instanceof User) {
					User u1 = (User)o1;
					User u2 = (User)o2;
					return Integer.compare(u1.getAge(), u2.getAge());
					
				}else {
					throw new RuntimeException("输入的数据类型不匹配");
				}
			}};
		TreeSet set = new TreeSet(com);
		set.add(new User("Tom",12));
		set.add(new User("jerry",22));
		set.add(new User("jim",31));
		set.add(new User("jack",45));
		set.add(new User("jack",66));
		
		Iterator iterator = set.iterator();
		while(iterator.hasNext()) {
			System.out.println(iterator.next());
		}
	}
}

User类和上面代码配套使用

public class User implements Comparable{
	private String name;
	private int age;
	public User() {
		super();
	}
	public User(String name, int age) {
		super();
		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 "User [name=" + name + ", age=" + age + "]";
	}
	@Override
	public int hashCode() {
		//return name.hashCode() + age;
		final int prime = 31;
		int result = 1;
		result = prime * result + age;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		System.out.println("User equals()....");
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		User other = (User) obj;
		if (age != other.age)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}
	//按照姓名从大到小排序
	@Override
	public int compareTo(Object o) {
		if(o instanceof User) {
			User user = (User)o;
//			return -this.name.compareTo(user.name);
			int compare = -this.name.compareTo(user.name);
			if(compare!=0) {
				return compare;
			}else {
				return Integer.compare(this.age, user.age);
			}
		}else {
			throw new RuntimeException("输入的类型不匹配");
		}
	}
	
}

面试题ArrayList、LinkedList、Vector三者的异同

相同点:

三个类都实现了List接口,存储数据的特点相同,存储有序的、可重复的数据;底层使用object[]elementData存储

不同点:

ArrayList:作为List接口的主要实现类,线程不安全,效率高
LinkedList:对于频繁的插入、删除操作,使用此类效率比ArrrayList高;底层使用双向链表存储
Vector:作为List接口古老实现类,线程安全的,效率低;底层使用object[]elementData存储

集合Collection中存储的如果是自定义的对象,需要自定义类重写那个方法?为什么?

equals()方法。contains()/remove()/retainsAll();
List:equals()方法
Set:(HashSet、LinkedHashSet为例):equals()、hashCode()
TreeSet:Comparable:CompareTo(Object obj)
Comparator:compare(Object o1,object o2)

set存储数据的特点是什么?常见的实现类有什么?说明一下彼此的特点。

HashSet对应HashMap
LinkedHashSet对应LinkedHashMap
TreeSet对应TreeMap

区分list中remove(int index)和remove(Object obj)

默认是在索引,newInteger删除对象2

public class ListExer {
	@Test
	public void testListRemove() {
		List list = new ArrayList();
		list.add(1);
		list.add(2);
		list.add(3);
		updateList(list);
		System.out.println(list);
	}
	
	private void updateList(List list) {
//		list.remove(2);
		list.remove(new Integer(2));
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值