社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
道德三皇五帝,功名夏后商周。
五霸七雄闹春秋,秦汉兴亡过手。
青史几行名姓,北邙无数荒丘。
前人田地后人收,说甚龙争虎斗
从上面的集合框架图可以看到,Java集合框架主要包括两种类型的容器,一种是集合(Collection),存储一个元素集合,另一种是图(Map),存储键/值对映射。Collection接口又有3种子类型,List、Set和Queue,再下面是一些抽象类,最后是具体实现类,常用的有ArrayList、LinkedList、HashSet、LinkedHashSet、HashMap、LinkedHashMap等等。
Collection接口是处理对象集合的根接口,其中定义了很多对元素进行操作的方法,AbstractCollection是提供Collection部分实现的抽象类。
Collection接口结构
常用的方法有:add()添加一个元素到集合中,addAll()将制定集合中的所有元素添加到集合中,contains()方法检测集合中是否包含指定的元素,toArray()方法返回一个表示集合的数组。
Collections是一个类,容器的工具类,就如同Arrays是数组的工具类
Collection是 Set List Queue和 Deque的接口
Queue: 先进先出队列
Deque: 双向链表
注:Collection和Map之间没有关系,Collection是放一个一个对象的,Map 是放键值对的
注:Deque 继承 Queue,间接的继承了 Collection
它可以定义一个允许重复的有序集合,从List接口中的方法来看,List接口主要是增加了面向位置的操作,允许在指定位置上操作元素,同时增加了一个能够双向遍历线性表的新列表迭代器ListIterator。AbstractList类提供了List接口的部分实现。AbstractSequentialList扩展自AbstractList,主要是提供对链表的支持。下面介绍List接口的两个重要的具体实现类,也是我们可能最常用的类,ArrayList和LinkedList。
//用来存放数据元素的数组
transient Object[] elementData;
//当前存储元素的个数
private int size;
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);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
//确定了添加元素后的大小之后将元素复制到新数组中
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);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0) // 删除某个元素时需要移动其它数组元素
System.arraycopy(elementData, index+1, elementData, index, numMoved);
elementData[--size] = null; // clear to let GC do its work
}
Iterator iterator = h.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。java hero.name="garen"; hero1.name="ketty"; ArrayList<Hero> heroArrayList = new ArrayList<>(); heroArrayList.add(hero); heroArrayList.add(hero1); for (Hero hh : heroArrayList) { System.out.println(hh.name); } for (int i = 0; i < heroArrayList.size(); i++) { System.out.println(heroArrayList.get(i).name); }
LinkedList同样实现了List接口,LinkedList还实现了双向链表结构Deque
可以很方便的在头尾插入删除数据.
//存储的元素个数
transient int size = 0;
//头节点
transient Node<E> first;
//尾节点
transient Node<E> last;
public boolean add(E e) {
linkLast(e);
return true;
}
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);//有趣
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
E unlink(Node<E> x) {
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev;
if (prev == null) {// 当前节点为头节点,重置头节点
first = next;
} else {//当前节点非头节点,将前驱节点和后继节点连接
prev.next = next;
x.prev = null;
}
if (next == null) {//当前节点为尾节点,重置尾节点
last = prev;
} else {// 当前节点非尾节点
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
modCount++;
return element;
}
名称 | 作用 |
---|---|
add | 增加一个元素 |
contains | 是否存在一个元素 |
get | 根据下表获取值 |
indexOf | 获取对象所处的位置 |
remove | 删除 |
set | 替换 |
size | 获取大小 |
toArray | 转换为数组 |
addAll | 把另一个容器的对象都加入进来 |
clear | 清空容器 |
对于Map而言,key是唯一的,不可以重复的。
安利一篇关于jdk1.8的HashMap详解
HashMap原理:使用数组加链表加红黑树,如果链表长度大于8,则将链表自动转换为红黑树结构,关于红黑树的原理精讲,暂且存着
进行键值映射
HashMap和HashSet都是无序的,具体顺序和JVM有关系
HashMap<String,Integer>map = new HashMap<>();
map.put("lihouhua",18);
map.keySet()//获取所有的key
map.values()//获取所有的values
map.get(str)
map.putAll(map)
map.clear();
原理,依赖HashMap函数,所有插入的元素作为键,值为final PRESENT
这也是没有重复元素的原因
所有的对象都有一个对应的hashcode,但并不是说所有的hashcode值肯定不一样,具体原理为hashcode本身是一个native方法
对于hashcode函数来说,有些JVM在实现时是直接返回对象的存储地址,但是大多时候并不是这样,只能说可能存储地址有一定关联。下面是HotSpot JVM中生成hash散列值的实现:
该实现位于hotspot/src/share/vm/runtime/synchronizer.cpp文件下。
static inline intptr_t get_next_hash(Thread * Self, oop obj) {
intptr_t value = 0 ;
if (hashCode == 0) {
// This form uses an unguarded global Park-Miller RNG,
// so it's possible for two threads to race and generate the same RNG.
// On MP system we'll have lots of RW access to a global, so the
// mechanism induces lots of coherency traffic.
value = os::random() ;
} else
if (hashCode == 1) {
// This variation has the property of being stable (idempotent)
// between STW operations. This can be useful in some of the 1-0
// synchronization schemes.
intptr_t addrBits = intptr_t(obj) >> 3 ;
value = addrBits ^ (addrBits >> 5) ^ GVars.stwRandom ;
} else
if (hashCode == 2) {
value = 1 ; // for sensitivity testing
} else
if (hashCode == 3) {
value = ++GVars.hcSequence ;
} else
if (hashCode == 4) {
value = intptr_t(obj) ;
} else {
// Marsaglia's xor-shift scheme with thread-specific state
// This is probably the best overall implementation -- we'll
// likely make this the default in future releases.
unsigned t = Self->_hashStateX ;
t ^= (t << 11) ;
Self->_hashStateX = Self->_hashStateY ;
Self->_hashStateY = Self->_hashStateZ ;
Self->_hashStateZ = Self->_hashStateW ;
unsigned v = Self->_hashStateW ;
v = (v ^ (v >> 19)) ^ (t ^ (t >> 8)) ;
Self->_hashStateW = v ;
value = v ;
}
value &= markOopDesc::hash_mask;
if (value == 0) value = 0xBAD ;
assert (value != markOopDesc::no_hash, "invariant") ;
TEVENT (hashCode: GENERATE) ;
return value;
}
对于使用到hashcode才能成立的数据结构,我们在使用equals函数之后,如果对于一个使用到hashcode的方法来说的话,我们同时需要对hashcode函数进行重写,不然相关函数会返回Null。
hashcode文章详解
clolections类是colllections对应的工具类,常用的类方法有
方法 | 简介 |
---|---|
reverse | 反转 |
shuffle | 混淆,顺序乱掉 |
sort | 排序 |
swap | 交换 |
rotate | 滚动 |
synchronizedList | 线程安全化,把非线程安全的类转化为线程安全类 |
泛型的用法是在容器后面添加Type可以是类,抽象类,接口,泛型表示这种容器,只能存放APHero,ADHero就放不进去了。
可以在一定程度上保证安全。
一般来说? extends Hero作为参数,而不需要直接初始化,
常用的来说,我们传入一个苹果类可以用 ? extends Fruitl
代替方法参数的表达式,从匿名类转化过来,匿名类
假设一个情景: 找出满足条件的Hero
本教程将从使用普通方法,匿名类,以及Lambda这几种方式,逐渐的引入Lambda的概念
普通方法:
public static int filter(ArrayList<Hero>heroes ){
int count = 0;
Iterator iterator = heroes.iterator();
for (int i = 0; i < heroes.size(); i++) {
if (heroes.get(i).hp>50){
count++;
}
}
return count;
}
匿名类:
首先继承接口,之后实现匿名方法
Hero_count hero_count = new Hero_count() {
@Override
public boolean hero_count(Hero hero) {
if (hero.hp>50)return true;
return false;
}
};
之后调用匿名类
private static int filter(ArrayList<Hero> arrayList, Hero_count hero_count) {
int count=0;
for (Hero h :
arrayList) {
if (hero_count.hero_count(h))count++;
}
return count;
}
Lambda的使用:---一种匿名方法
调用方式相同,传入的参数不同
传入的参数为h->h.hp>50
Hero_count c = h->h.hp>50
private static int filter(ArrayList<Hero> arrayList, Hero_count hero_count) {
int count=0;
for (Hero h :
arrayList) {
if (hero_count.hero_count(h))count++;
}
return count;
}
InetAddress host = InetAddress.getLocalHost();
String ip = host.getHostAddress();
Process p = Runtime.getRuntime().exec("ping "+"192。168.2.106");
BufferedReader br - new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = null;
StringBuilder sb = new StringBuilder();
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!