java学习:集合


1、集合框架概述

java中为了操作多个对象,需要对多个对象进行存储和管理,针对Array的局限性,java集合就像一个容器可以把多个对象的引用放入容器中。

java集合类就是对多个数据进行存储和操作的类,存储的类型可以是单个对象,也可以是关联关系的对象。主要此时的存储是在内存中,临时存储。

2、分类

java集合分为两种不同的存储体系:

  • Collection(接口):单值操作接口

    • List(接口):元素有序,可以重复
      • ArraysList、LinkList、Vector
    • Set(接口): 元素无序,不可以重复
      • HashSet、LinkedHashSet、TreeSet
  • Map:对值操作接口,由key-value组成,key不能重复,value可以重复,元素(key-value)无序

    • hashMap、LinkedHashMap、Hashtable TreeMap Properties
2.1 Collection
2.1.1 体系

在这里插入图片描述

JDK不提供次接口的具体实现,而是提供更好的具体的子接口;

JDK5之前,Java集合会丢失容器中所有对象的数据类型,把所有的对象都当成Object类型处理,从JDK1.5之后增加了泛型,集合就可以记住容器中对象的数据类型。

2.1.2 常用方法
  • add():添加元素
  • size():元素个数
  • addAll(Collection c):将c中元素放入本集合
  • clear():清空集合所有元素
  • isEmpty():集合元素是否为空
  • contains(Object o):判断集合中是否包含指定元素,实际调用equals判断是否存在。
  • containsAll(Collection<?> c):判断集合c中的元素是否在指定的集合中。
  • remove(Object o):移除元素,位置前移,根据equals方法判断
  • removeAll(Collection <?>c):从当前集合中删除c中所有的元素。
  • retainAll(Collection<?> c):交集,当前集合变化,返回true、false。
  • equals(Object o):与集合比较。
  • hashCode():返回hashCode值
  • toArrays():返回数组
  • toArray(T[] ):返回指定类型数组
  • asList():返回的集合是Arrays中的内部类定义的ArrayList

向Collection接口的实现类中添加对象时,要求所在的类需要重写equals方法。

collection集合判断元素是否存在会调用对象所在的equals方法。

2.1.3 迭代器接口Iterator

Iterator对象迭代器(设计模式),主要用于遍历集合中的元素。

迭代器是为容器而生的。迭代器创建的前提必须是集合要存在,通过集合创建迭代器。

Collection接口继承了java.lang.Iterable接口,iterator()方法仅仅用于遍历集合,Iterator本身并不提供承装对象的能力。

集合对象每次调用iterator()方法都会得到一个新的迭代器对象,默认游标都在集合的第一个元素之前。

迭代器执行原理:

在这里插入图片描述

Collection col = new ArrayList();
col.add(123);
col.add(456);
col.add(new String("Tom"));
col.add(false);
//col.add(new String("Tom"));
Person p = new Person(20,"QQ");
col.add(p);

//1、创建迭代器
Iterator iter = col.iterator();

//方式一:直接获取元素
System.out.println(iter.next());//123
System.out.println(iter.next());//456
System.out.println(iter.next());//tom
System.out.println(iter.next());//fase
System.out.println(iter.next());//Person [name=QQ, age=20]
System.out.println(iter.next());//报错,NoSuchElementException

//方式二:
while(iter.hasNext()) {
	System.out.println(iter.next());
}

//迭代器理解: 每次next,游标下移一次,会跳过元素 (报错)
while(iter.next() != null) {
	System.out.println(iter.next());
}

//错误,死循环
while(iter.hasNext()) { //游标没有移动
	System.out.println(col.iterator().next()); //每次调用iterator()产生新的迭代器
}

//删除remove
Iterator iterator = coll.iterator();
while(iterator.hasNext()){
  //iterator.remove(); //报错:IllegalStateException
  Object object = iterator.next();
  if("Tom".equals(object)){
    	iterator.remove();
   		//iterator.remove();报错
 }
}
System.out.println(coll);

Iterator可以删除集合中的元素,但是在遍历过程中通过迭代器对象的remove()方法不是集合中的remove方法。

如果还未调用next()方法或多次调用remove方法都会报错。

2.1.4 foreach循环

jdk1.5之后提供了foreach循环迭代访问Collection和数组。

foreach底层使用的迭代器方式。

Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(new String("Tom"));
coll.add(new String("Jack"));
coll.add(false);
//for(数据类型 变量:数组/集合)
for(Object obj : coll){
  System.out.println(obj);
}
int[] arr = new int[]{1,2,3,4,5};
//方式一:
for (int i = 0; i < arr.length; i++) {
  System.out.println(arr[i]);
}
//方式二:增强for循环
for(int i : arr){
  System.out.println(i);
}
2.1.5 List
(1)概述

有序、可重复、动态数组,某些场合下代替原有的数组。

核心实现类:ArrayList、LinkedList、Vertor

(2) ArrayList

作为List接口的主要实现类,线程不安全、效率高,底层使用Object[] elementDate数组存储数据。

源码:

/**
* Constructs an empty list with an initial capacity of ten.
*/
//创建一个空数组,保存数据
public ArrayList() {
  this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

//保存数据的数组
transient Object[] elementData;

//添加的方法
public boolean add(E e) {
  //数组扩容的方法
  ensureCapacityInternal(size + 1);
 // Increments modCount!!
  //将元素放入数组中
  elementData[size++] = e;
  return true;
}
private void ensureExplicitCapacity(int minCapacity) {
  modCount++;
  // overflow-conscious code
  if (minCapacity - elementData.length > 0)
   //改变数组大小(容量),扩容方法
      grow(minCapacity);
}

ArrayList底层使用Object[] elementData初始化为{},并没有创建长度为10的数组(Jdk1.8)。

第一次调用add()方法后,底层才创建了长度为10的数组。

Arrays.asList(…)方法返回的List集合,既不是ArrayList的实例,也不是Vector的实例,返回的是一个固定长度的List集合。

常用方法:

add(int index, E element) : 指定位置添加元素(index之后元素后移)

get(int index)

indexOf(Object o):根据指定对象寻找下标,判断标准equals

lastIndexOf(Object o) :获取最后一次元素出现的下标

remove(int index) :根据下标删除元素,返回删除的元素

set(int index, E element) : 修改

subList(int fromIndex, int toIndex):获取指定区间的子集左闭区间[ )右开区间

(3) LinkedList

底层使用双向表存储数据,如果频繁添加,删除元素效率比ArrayList高。链表如图:

在这里插入图片描述

/**
  * Pointer to first node.
  * Invariant: (first == null && last == null) ||
  *      (first.prev == null && first.item != null)
  */
  transient Node<E> first; //第一个节点
  /**
  * Pointer to last node.
  * Invariant: (first == null && last == null) ||(last.next == null && last.item != null)
*/
  transient Node<E> last; //最后一个节点
//node的内部结构
private static class Node<E> {
  E item;
  Node<E> next; //下一个node
  Node<E> prev; //前一个node
  Node(Node<E> prev, E element,Node<E> next) {
    this.item = element;
    this.next = next;
    this.prev = prev;
 }
}
//添加的逻辑
public boolean add(E e) {
    linkLast(e);
    return true;
 }

LinkedList内部声明了Node类型的first和last属性,默认值null;

add()方法,将参数封装到Node中,创建了Node对象;

Node的创建,体现了LinkedList双向列表的结构。

独有方法:

LinkedList linkedList = new
LinkedList();
linkedList.add(123);
linkedList.add(456);
linkedList.add("tom");

//addFirst(E e)
//linkedList.addFirst("AA");

// addLast(E e)
// linkedList.addLast("ZZ");
// System.out.println(linkedList);
//peek()
//System.out.println(linkedList.peek());
//peekFirst()
//System.out.println(linkedList.peekFirst());
//poll()
System.out.println(linkedList.poll());
System.out.println(linkedList);
(4) Vertor

Vertor是一个古老的实现类,甚至出现在List接口之前,线程安全(synchronized),效率低,底层使用Object[] elementDate数组实现存储。

protected Object[] elementData; //使用数组保存数据
//初始大小10
public Vector() {
    this(10);
 }
//扩容方法
public synchronized boolean add(E e) {
    modCount++;
 
 ensureCapacityHelper(elementCount + 1);
    elementData[elementCount++] = e;
    return true;
 }

默认扩容是原来的2倍

(5) 总结

常用方法:

增:add(Object obj)

删:remove(int index) 、remove(Object obj)

改:set(int index, Object obj)

查:get(int index)

插入:add(int index, Object obj)

长度:size()

遍历:Iterator方式、增强for循环、普通for循环

遍历集合:

ArrayList list = new ArrayList(); //可以
指定初始容量
list.add(123);
list.add(456);
list.add(new String("AA"));
list.add(new String("BB"));
list.add(new String("CC"));

//方式一:迭代器
Iterator iter = list.iterator();
while(iter.hasNext()) {
  Object obj = iter.next();
  System.out.println(obj);
}
//方式二:迭代器+for
for (Iterator iter2 = list.iterator();iter2.hasNext();) {
  Object obj = iter2.next();
  System.out.println(obj);
}
//方式三:for+get()方法
for (int i = 0; i < list.size(); i++) {
  Object obj = list.get(i);
  System.out.println(obj);
}
//方式四:foreach
for(Object obj : list) {
  System.out.println(obj);
}
2.1.6 Set

存储无序的,不可重复的数据。

核心实现类:

  • HashSet:
    • LinkedHashSet
  • TreeSet
(1) HashSet

作为Set的主要实现类,线程不安全,可以存储null值。

存储原理:

  • 当向 HashSet 集合中存入一个元素时,HashSet会调用该对象的 hashCode() 方法来得到该对象的 hashCode 值,然后根据 hashCode 值,通过某种散列函数决定该对象在 HashSet 底层数组中的存储位置。(这个散列函数会与底层数组的长度相计算得到在 数组中的下标,并且这种散列函数计算还尽可能保证能均匀存储元素,越是散列分布, 该散列函数设计的越好)。

  • 如果两个元素的hashCode()值相等,会再继续调用equals方法,如果equals方法结果 为true,添加失败;如果为false,那么会保存该元素,但是该数组的位置已经有元素了, 那么会通过链表的方式继续链接。

  • 如果两个元素的 equals() 方法返回 true,但它们的 hashCode() 返回值不相等,hashSet 将会把它们存储在不同的位置,但依然可以添加成功(违背了set集合不能放重复元素的规则)。

//Set判断元素相同
Set set = new HashSet();
set.add(123);
set.add(456);
set.add("Tom");
set.add(123); //相同元素不能添加
//new Object().hashCode();
//判断两个元素是否相同,先判断hashcode值,再调用equals方法
set.add(new Person("Tom",20));
set.add(new Person("Tom",20));
System.out.println(set);

结论:

  1. Set接口是Collection的子接口,set接口没有提供额外的方法
  2. HashSet 按 Hash 算法来存储集合中的元素,因此具有很好的存取、查找、删除性能
  3. HashSet 集合判断两个元素相等的标准:两个对象通过 hashCode() 方法比较相等,并且两个对
    象的 equals() 方法返回值也相等。
  4. 对于存放在Set容器中的对象,对应的类一定要重写equals() 和hashCode(Objectobj) 方法,以
    实现对象相等规则 。即: “相等的对象必须具有相等的散列码“。
  5. 无序性:表示存储的数据在底层的数组中并非按照数组的索引顺序添加,而是根据数据的哈希值决定。
  6. 不可重复:为了保证元素的不可重写,我们需要重写hashcode和equals方法。
(3)LinkedHashSet

LinkedHashSet是HashSet的子类。

LinkedHashSet根据元素的hashCode值来决定元素的存储位置,但同时使用双向链表来维护元
素的次序,这使得元素看起来是以添加的顺序保存的。

LinkedHashSet插入的性能略低于HashSet,但迭代访问set的性能较好。

LinkedHashSet不能放入重复元素。

(3)TreeSet
  • TreeSet是SortedSet接口的实现类,TreeSet可以确保元素的集合处于有序状态

  • TreeSet底层使用红黑树结构存储

  • 判断两个元素是否重复的标准:

    • 自然排序使用: compareTo(Object obj)==0判断相等,不再使用equals()方法。
    • 定制排序使用:compare(Object o1,Object o2)判断相等, 不再使用equals()方法。
    • 当需要把一个对象放入 TreeSet 中,重写该对象对应的 equals() 方法时,应保证该方法与compareTo(Object obj) 方法有一致的结果:如果两个对象通过equals()方法比较返回 true,则通过compareTo(Object obj) 方法比较应返回0。
//自然排序
class Person implements
Comparable<Person> {
private String name;
private int 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) {
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;
}
@Override
public String toString() {
return "Person [name=" + name +
", age=" + age + "]";
}
@Override
    public int compareTo(Person o) {
if(this.age > o.age) {
return 1;
}else if(this.age < o.age) {
return -1;
}else {
//return 0;
return
this.name.compareTo(o.name);
}
}
}
TreeSet<Person> set = new
TreeSet<Person>();
set.add(new Person("Tom", 12));
set.add(new Person("Jim", 30));
//相同的判断标准,comparable返回的值
set.add(new Person("Jack", 30));
set.add(new Person("Jack", 30)); //相同不能添加
set.add(new Person("Jerry", 20));
System.out.println(set);

定制排序:

Comparator<Person> com = new
Comparator<Person>() {
  @Override
  public int compare(Person o1, Person
o2) {
    if(o1.getAge() > o2.getAge()) {
      return 1;
   }else if(o1.getAge() <
o2.getAge()) {
      return -1;
   }else {
      return
o1.getName().compareTo(o2.getName());
   }
 }
};
TreeSet<Person> set = new
TreeSet<Person>(com);
set.add(new Person("Tom", 12));
set.add(new Person("Jim", 30));
//相同的判断标准,comparable返回的值
set.add(new Person("Jack", 30));
set.add(new Person("Jack", 30)); //相同不能添加
set.add(new Person("Jerry", 20));
System.out.println(set);
2.2 Map接口

双列数据。存储key-value的对数据

2.2.1 特点

key无序、不可重复,可以使用Set存储所有的key,如果是引用类型需要重写hashCode和equals方法,保证不可重复;

value无序,可重复,可以使用Collection存储所有的value —>如果是引用类型需要重写equals方法。

一个键值对:key-value就构成了一个Map.Entry对象Map中的Entry无序,不可重复,使用Set可以存储Entry。

2.2.2 核心实现类
  • HashMap
    • LinkedHashMap
  • treeMap
  • Hashtable
    • properties
(1) HashMap

作为Map的主要实现类,线程不安全,效率高,存储null的key-value,底层使用数组+链表(jdk7)、数组+链表+红黑树(jdk1.8)。

在这里插入图片描述

(2)LinkedHashMap

保证遍历Map元素时,可以按照添加的顺序实现遍历,因为在原有的HashMap底层结构基础上添加了一对指针,指向前一个和后一个元素,对于频繁的操作,执行效率高于hashMap。

在HashMap存储结构的基础上,使用了一对双向链表来记录添加元素的顺序。与LinkedHashSet类似,LinkedHashMap可以维护Map的迭代顺序,迭代顺序与key-value对的插入顺序一致。

//HashMap内部的Node节点
static class Node<K,V> implements
Map.Entry<K,V> {
  final int hash;
  final K key;
  V value;
  Node<K,V> next;
   /**
  * HashMap.Node subclass for normal
LinkedHashMap entries.
  */
  static class Entry<K,V> extends
HashMap.Node<K,V> {
    Entry<K,V> before, after; //使用双向链表
    Entry(int hash, K key, V value,Node<K,V> next) {
      super(hash, key, value,next);
   }
(3) Hashtable

作为一个古老的实现类,线程安全,效率低,不能存储null的key和value。

(4) Properties

是Hashtable的子类,主要用于属性文件的配置。

由于属性文件里的key、value都是String类型,所以Properties对象的key和value也是String类型。

存取数据时,建议使用setProperties(String key,String value)保存数据,getProperties(String key)取数据。

// 1、创建流
FileInputStream fis = null;
try {
    fis = new FileInputStream("db.properties");
    // 2、创建Properties对象
    Properties p = new Properties();
    // 3、加载流对应的信息
    p.load(fis);
    //4、根据key获取value
    String username = p.getProperty("username");
    String password = p.getProperty("password");
    String url = p.getProperty("url");
    System.out.println("username = " + username);
    System.out.println("password = " + password);
    System.out.println("url = " + url);
	} finally {
    	if(fis != null) {
        fis.close();
    }
}
(5) TreeMap
  • TreeMap可以保证所有的key-value处于有序状态

  • TreeMap底层使用红黑树结构保存数据

  • TreeMap的key排序

    • 自然排序:TreeMap中所有的key必须实现Comparable接口,而且所有的key为
      同一类型对象。
    • 定制排序:创建TreeMap对象时,传递一个Comparator对象。
//
class Person implements Comparable<Person> {
private String name;
private int 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) {
    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;
    }
    
@Override
public String toString() {
	return "Person [name=" + name +", age=" + age + "]";
}
    
@Override
public int compareTo(Person o) {
	if(this.age > o.age) {
		return 1;
	}else if(this.age < o.age) {
		return -1;
	}else {
		//return 0;
		return this.name.compareTo(o.name);
	}
}
}
TreeMap map = new TreeMap();
map.put(new Person("Tom",20), 20);
map.put(new Person("Jack",30), 20);
map.put(new Person("Carry",40), 20);
map.put(new Person("Tony",10), 20);

定制排序:

Comparator<Person> com = new Comparator<Person>() {
    @Override
    public int compare(Person o1, Person o2) {
        if(o1.getAge() > o2.getAge()) {
          	return -1;
       	}else if(o1.getAge() < o2.getAge()) {
          	return 1;
       	}else {
          	return o1.getName().compareTo(o2.getName());
       	}
    }
}
TreeMap map = new TreeMap(com);
map.put(new Person("Tom",20), 20);
map.put(new Person("Jack",30), 20);
map.put(new Person("Carry",40), 20);
map.put(new Person("Tony",10), 20);
2.2.3 常用方法
Map map = new HashMap();
//添加put
map.put("AA",123);
//修改put
map.put("AA",456);
//添加putAll
Map map2 = new Map().put("BB",123);
map.putAll(map2);

//删除remove(Key k),根据key删除元素,返回值为value
map.remove("AA");
//remove(Key k,Value v)
//clear()  
//get(Key k),返回值value
//containsKey(Object key): 判断是否存在key
//containsValue(Object value):判断是否存在value
//遍历
//1、遍历所有的key ---- keySet()获取所有的key
Set set = map.keySet();
Iterator iter = set.iterator();
while(iter.hasNext()) {
System.out.println(iter.next());
}
System.out.println("------------");

//2、遍历所有的value ----
values():获取所有的value
    Collection collection =map.values();
	for(Object obj : collection) {
		System.out.println(obj);
	}
System.out.println("------------");

//3、遍历所有的key---value entrySet()获取所有的Entry
Set set2 = map.entrySet();
//set集合中装的是Entry --- key:value
Iterator iter2 =set2.iterator();
    while(iter2.hasNext()) {
        Object obj = iter2.next();
        //此时object = Entry
        Entry entry = (Entry)obj;
        //System.out.println(obj);
	System.out.println(entry.getKey() + "---" + entry.getValue());
}
System.out.println("-------------");
 
//方式二
Set keySet = map.keySet();
for (Iterator iter3 = keySet.iterator(); iter3.hasNext();) {
    Object key = iter3.next();//key
	Object value =map.get(key); //
	System.out.println(key + "---" + value);
	}
}

总结:
增: put(Object key, Object value)
删:remove(Object key)
改:put(Object key, Object value)
查:get(Object key)
长度:size()
遍历:keySet()、values()、entrySet()

3、Collections工具类

Collections是一个操作List、set和Map等集合的工具类

提供的许多的静态方法,对集合进行排序,查询,修改等操作,可以将集合对象设置为不可
变,实现同步方法等操作。
常用方法:
public static void sort(List list):排序 默认情况下是自然顺序。
public static int binarySearch(List<?> list,T key):二分查找
public static T max(Collection<?> coll):最大值
public static void reverse(List<?> list):反转
public static void shuffle(List<?> list):随机置换
addAll(Collection<? super T> c, T… elements):添加元素
copy(List<? super T> dest, List<? extends T> src):将所有元素从一个列表复制到另一个列表中。
disjoint(Collection<?> c1, Collection<?> c2):如果两个指定的集合没有共同的元素,则返回 true 。
fill(List<? super T> list, T obj):用指定的元素代替指定列表的所有元素。
frequency(Collection<?> c, Object o):返回指定集合中与指定对象相等的元素数。
indexOfSubList(List<?> source, List<?> target):返回指定源列表中指定目标列表的第一次出现的起始位置,如果没有,则返回-1。
lastIndexOfSubList(List<?> source, List<?> target):返回指定源列表中指定目标列表的最后一次出现的起始位置,如果没有则返回-1。
replaceAll(List list, T oldVal, T newVal):将列表中一个指定值的所有出现替换为另一个。

List<Integer> list = new ArrayList<Integer>();
		list.add(20);
		list.add(50);
		list.add(10);
		list.add(60);
		list.add(50);
		
		List<Integer> list2 = new ArrayList<Integer>();
		list2.add(100);
		list2.add(50);
		list2.add(30);
		
		Collections.sort(list);;//[10, 20, 50, 50, 60]
		System.out.println(list);
		
		int i = Collections.binarySearch(list, 50);
		System.out.println(i);//2
		
		int max = Collections.max(list);
		System.out.println(max);//60
		
		Collections.reverse(list);
		System.out.println(list);//[60, 50, 50, 20, 10]
		
		Collections.shuffle(list);
		System.out.println(list);//[10, 60, 50, 20, 50]  [50, 10, 60, 20, 50]
		
		Collections.addAll(list, 5,6,70);
		System.out.println(list);//[50, 10, 50, 60, 20, 5, 6, 70]
		
		Collections.copy(list, list2);//list2的size要小于list的size
		System.out.println(list);//[100, 50, 30, 50, 10, 5, 6, 70]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值