杂谈之内卷
最近在几个技术交流群经常看到大家讨论开发行业内卷,什么是内卷呢?一句话,大家越来越努力,越来越累,但是幸福感和收获并没有增加,甚至实际上是下降了。
举个大城市西安的例子
为了让小孩能上更好的小学,家长都发疯一样买学区房,但是好学校一年的招生名额是有限的,不管学区房有多贵,并不能增加招生名额。比如某名校一年收250个小学生,就算周边学区房涨价到3万一平米,还是一年收250个学生,但家长们为了让孩子入学,不得不接受3万一平米的单价,去买一套老破小居住,说不定还要把原先的大房子卖掉,因为限购了,名下只能有一套房。
更可怕的是,学区房政策是会变的,说不定每年都变,所以你就算买了学区房也不一定能够顺利上学。比如,周边适龄儿童有400个人,可是招生名额还是只有250,那就意味着肯定有150人无法入学。那怎么办呢?学校就搞了个排名,有本地户籍且人户一致的优先入学,有本地户籍人户不一致的排第二,没有本地户籍但有房产证的排第三……如果以上这些都卡不住你怎么办呢?很简单,多校划片,最终能上哪个学校,电脑随机抽签。
但事实上考大学一样是内卷游戏,内卷给人一种恐慌的感觉,大家在恐惧的驱使下,竭尽所能争夺资源,可是资源有限,大家越是争夺,资源损坏越大,甚至无序的竞争会带来毁灭性结局。可是你不争,就被别人得了便宜,被人骂SB。
社会发展到今天,凭现在的生产力,足以让所有人过上衣食无忧的生活。但是由于分配问题,由于资本的压榨本质,使得大部分人并没有因为生产力和科技的发展获得相应的幸福感。内卷的结果,是资本可以获得更加廉价的劳动力,资本增值更快。但是劳动力本身却更苦了。尤其是在这个科技和知识是第一生产力的时代,内卷并不利于社会进步。因为知识生产需要一个比较宽松的社会环境。社会福利越好,知识生产反而会越佳。那种残酷竞争所带来的不安全感,实际上是不利于科技进步的。内卷状态下的知识生产者,是非常焦虑的。相应的,知识学习者也是极度焦虑,这样的学习效果其实并不好。这更何况,这种过分的竞争极大的降低了人们的安全感和幸福感。……我所想象的理想世界是,每个人可以在解决温饱的前提下,做自己喜欢做的工作,焕发出最大的工作热情,从而促进社会进步。当然,这只是理想。
我不知道如何改变内卷的命运,但可以尽量在内卷的前头早做准备,2020年虽然是非常艰难的一年,但相对于未来而言,反倒可能是最容易的一年了,因为将来随着内卷的深入,一定会越来越困难的,别到时候又后悔错过了2020年的机会。种一个颗树最好的时间一个是十年前,另一个就是现在。机会也永远留给有准备的人,你不去学习不去进步的话,到时候又要做负责接盘的下家了。
正文
最近,团队里边一个兄弟突然叫我:快来看,有个奇怪的事情,无法解释…
跑过去一看,是这么一段代码:
private static class Person { private int age; private String name; public Person(int age, String name) { this.age = age; this.name = name; } public void setAge(int age) { this.age = age; } @Override public String toString() { return name + ":" + age; } } @Test public void test_collections2_filter() { Person lxy = new Person(35, "lxy"); Person xhf = new Person(34, "xhf"); Person nws = new Person(31, "nws"); List<Person> names = Lists.newArrayList(lxy, xhf, nws); Collection<Person> personAgeOver30 = Collections2.filter(names, p -> p.age > 30); System.out.println(personAgeOver30);//[lxy:35, xhf:34, nws:31] nws.setAge(25); System.out.println(personAgeOver30);//[lxy:35, xhf:34] }
确实是比较奇怪,personAgeGt30中的元素怎么会少了一个呢?
本着任何表现奇怪的程序都有其背后原因的指导思想,打开了Guava Collections2类的源代码。
其实,源代码的注释已经解释得非常清楚了:returned collection is a live view of {@code unfiltered};
changes to one affect the other.
/** * Returns the elements of {@code unfiltered} that satisfy a predicate. The returned collection is * a live view of {@code unfiltered}; changes to one affect the other. * * <p>The resulting collection's iterator does not support {@code remove()}, but all other * collection methods are supported. When given an element that doesn't satisfy the predicate, the * collection's {@code add()} and {@code addAll()} methods throw an {@link * IllegalArgumentException}. When methods such as {@code removeAll()} and {@code clear()} are * called on the filtered collection, only elements that satisfy the filter will be removed from * the underlying collection. * * <p>The returned collection isn't threadsafe or serializable, even if {@code unfiltered} is. * * <p>Many of the filtered collection's methods, such as {@code size()}, iterate across every * element in the underlying collection and determine which elements satisfy the filter. When a * live view is <i>not</i> needed, it may be faster to copy {@code Iterables.filter(unfiltered, * predicate)} and use the copy. * * <p><b>Warning:</b> {@code predicate} must be <i>consistent with equals</i>, as documented at * {@link Predicate#apply}. Do not provide a predicate such as {@code * Predicates.instanceOf(ArrayList.class)}, which is inconsistent with equals. (See {@link * Iterables#filter(Iterable, Class)} for related functionality.) * * <p><b>{@code Stream} equivalent:</b> {@link java.util.stream.Stream#filter Stream.filter}. */
Collections2.filter方法返回的只是原有列表的一个视图。所以:改变被过滤列表会影响过滤后列表,反之
亦然。并且我们从这段文字中还能get到下面几个注意事项:
过滤后列表的迭代器不支持remove()操作
add不符合过滤条件的元素,会抛出IllegalArgumentException
当过滤后列表中的元素不再满足过滤条件时,会影响到已经过滤出来的列表出于程序员的本能,决定还是看下源码心里更踏实。核心源码如下:
1. add方法: public boolean add(E element) { //不符合过滤条件抛IllegalArgumentException的原因 checkArgument(predicate.apply(element)); return unfiltered.add(element); } 不支持通过迭代器删除的原因: public abstract class UnmodifiableIterator<E> implements Iterator<E> { /** Constructor for use by subclasses. */ protected UnmodifiableIterator() {} /** * Guaranteed to throw an exception and leave the underlying data unmodified. * * @throws UnsupportedOperationException always * @deprecated Unsupported operation. */ @Deprecated @Override public final void remove() { throw new UnsupportedOperationException(); } 打印结果变化的原因: public String toString() { Iterator<E> it = iterator(); if (! it.hasNext()) return "[]"; StringBuilder sb = new StringBuilder(); sb.append('['); for (;;) { E e = it.next(); sb.append(e == this ? "(this Collection)" : e); if (! it.hasNext()) return sb.append(']').toString(); sb.append(',').append(' '); } } @Override public Iterator<E> iterator() { return Iterators.filter(unfiltered.iterator(), predicate); }
问题已经弄清楚了,怎么修改这个问题呢?
方案一(可行):
干脆不用guava,
List<Person> names = Lists.newArrayList(lxy, xhf, nws); ArrayList<Person> persons = new ArrayList<>(); for (Person p : names) { if(p.age > 30) { persons.add(p); } }
方案二(可行):
用个容器再包装一下:
Collection<Person> personAgeGt30 = new ArrayList<(Collections2.filter(names, p -> p.age > 30));
方案三(可行,改用Java8的过滤器):
List<Person> personAgeGt30 = names.stream().filter((Predicate<Person>) p -> p.age >30).collect(Collectors.toList());
方案四(可行,改用Guava的连贯接口,IDEA编辑器会提示你替换成Java8 API)
ImmutableList<Person> personAgeGt30 = FluentIterable.from(names).filter(p -> p.age > 30).toList();
上述方案中,支持java8的生产环境推荐方案三,不支持java8的生产环境推荐方案二。
总结
其实,Java语言中类似的坑还有很多,比如:
- 1.Arrays.asList()生成的列表是不可变的。
- 2.subList生成的子列表,对原列表元素的修改,会导致子列表的遍历、增加、删除抛出ConcurrentModificationException,
- 3.subList对子列表的修改会影响到原列表数据
- 4.修改Map的keySet()方法和values()生成的容器,都会影响Map本身。
总之,使用任何API之前多看看源码,至少看看源码的注释。
全部评论
(3) 回帖