This与我的问题不重复。我检查了它,它更多的是关于内部匿名类。
我对Lambda表达式感到好奇并测试了以下内容:
在我不知道自己会想出什么的事实中,第一批结果并不令人惊讶:
final int NUMBER_OF_LIST_INDEXES = 10_000;
List<String> myList = new ArrayList<>();
String[] myWords = "Testing Lamba expressions with this String array".split(" ");
for (int i = 0 ; i < NUMBER_OF_LIST_INDEXES ; i++){
myList.add(myWords[i%6]);
}
long time = System.currentTimeMillis();
// BOTH TESTS WERE RUN SEPARATELY OF COURSE
// PUT THE UNUSED ONE IN COMMENTS WHEN THE OTHER WAS WORKING
// 250 milliseconds for the Lambda Expression
myList.removeIf(x -> x.contains("s"));
// 16 milliseconds for the traditional Loop
for (int i = NUMBER_OF_LIST_INDEXES - 1 ; i >= 0 ; i--){
if (myList.get(i).contains("s")) myList.remove(i);
}
System.out.println(System.currentTimeMillis() - time + " milliseconds");
但是,我决定将常数NUMBER_OF_LIST_INDEXES
更改为一百万,结果如下:
final int NUMBER_OF_LIST_INDEXES = 1_000_000;
List<String> myList = new ArrayList<>();
String[] myWords = "Testing Lamba expressions with this String array".split(" ");
for (int i = 0 ; i < NUMBER_OF_LIST_INDEXES ; i++){
myList.add(myWords[i%6]);
}
long time = System.currentTimeMillis();
// BOTH TESTS WERE RUN SEPARATELY OF COURSE
// PUT THE UNUSED ONE IN COMMENTS WHEN THE OTHER WAS WORKING
// 390 milliseconds for the Lambda Expression
myList.removeIf(x -> x.contains("s"));
// 32854 milliseconds for the traditional Loop
for (int i = NUMBER_OF_LIST_INDEXES - 1 ; i >= 0 ; i--){
if (myList.get(i).contains("s")) myList.remove(i);
}
System.out.println(System.currentTimeMillis() - time + " milliseconds");
为了使事情更简单,以下是结果:
| | 10.000 | 1.000.000 |
| LAMBDA | 250ms | 390ms | 156% evolution
|FORLOOP | 16ms | 32854ms | 205000+% evolution
我有以下问题:
这背后的魔力是什么?当使用的索引是* 100时,我们如何为数组而不是lambda带来如此大的差异。
在性能方面,我们如何知道何时使用Lambdas以及何时坚持使用传统方式处理数据?
这是List
方法的特定行为吗?其他Lambda表达式是否也会产生像这样的随机性能?
答案 0 :(得分:13)
因为public class MainActivityFragment extends Fragment {
private ArrayAdapter<String> mForecastAdapter;
public MainActivityFragment() {
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
String[] forecastArray = {
"Today - Foggy - 12/3",
"Tomorrow - Rainy - 7/4",
"Wed - Sunny - 12/3",
"Thurs - Cloudy - 10/4",
"Fri - Rainy - 12/8",
"Sat - Heavy Rain- 10/5",
"Sun - Sunny - 32/23"
};
List<String> weekforecast = new ArrayList<String>(
Arrays.asList(forecastArray));
mForecastAdapter =
new ArrayAdapter<String>(
getActivity(),
R.layout.list_item_forecast,
R.id.list_item_forecast_textview,
weekforecast);
ListView listView = (ListView) rootView.findViewById(R.id.list_item_forecast);
listView.setAdapter(mForecastAdapter);
return rootView;
}
public class FetchWeatherTask extends AsyncTask<Void, Void, Void>
{
private final String LOG_TAG = FetchWeatherTask.class.getSimpleName();
@Override
protected Void doInBackground(Void... params) {
HttpURLConnection urlConnection = null;
BufferedReader reader = null;
// Will contain the raw JSON response as a string.
String forecastJsonStr = null;
try {
// Construct the URL for the OpenWeatherMap query
// Possible parameters are avaiable at OWM's forecast API page, at
// http://openweathermap.org/API#forecast
URL url = new URL("api.openweathermap.org/data/2.5/find?q=7000&mode=json&units=metric&ch+7&appid=bd82977b86bf27fb59a04b61b657fb6f");
// Create the request to OpenWeatherMap, and open the connection
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.connect();
// Read the input stream into a String
InputStream inputStream = urlConnection.getInputStream();
StringBuffer buffer = new StringBuffer();
if (inputStream == null) {
// Nothing to do.
return null;
}
reader = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = reader.readLine()) != null) {
// Since it's JSON, adding a newline isn't necessary (it won't affect parsing)
// But it does make debugging a *lot* easier if you print out the completed
// buffer for debugging.
buffer.append(line + "\n");
}
if (buffer.length() == 0) {
// Stream was empty. No point in parsing.
return null;
}
forecastJsonStr = buffer.toString();
} catch (IOException e) {
Log.e("PlaceholderFragment", "Error ", e);
// If the code didn't successfully get the weather data, there's no point in attemping
// to parse it.
return null;
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
if (reader != null) {
try {
reader.close();
} catch (final IOException e) {
Log.e("PlaceholderFragment", "Error closing stream", e);
}
}
}
return null;
}
}
非常昂贵:)它需要复制和移动其余的元素,这在你的情况下会多次完成。
虽然remove(index)
不需要这样做。它可以扫描一次并标记要删除的所有元素;然后最后阶段将幸存者复制到列表头部一次。
答案 1 :(得分:8)
我写了一个JMH基准来测量它。它有4种方法:
removeIf
ArrayList
。removeIf
LinkedList
。iterator.remove()
上使用ArrayList
的iterator.remove()
上使用LinkedList
的基准测试的目的是表明removeIf
和迭代器应该提供相同的性能,但ArrayList
不是这种情况。
默认情况下,removeIf
在内部使用迭代器来删除元素,因此我们应该期望removeIf
和iterator
具有相同的效果。
现在考虑一个ArrayList
,它在内部使用一个数组来保存元素。每当我们调用remove
时,索引后面的其余元素必须移动一个;所以每次都要复制很多元素。当迭代器用于遍历ArrayList
并且我们需要删除元素时,这种复制需要一次又一次地发生,这使得它非常慢。对于LinkedList
,情况并非如此:删除元素时,唯一的变化是指向下一个元素的指针。
那么为什么removeIf
ArrayList
与LinkedList
一样快?因为它实际上被覆盖并且它不使用迭代器:代码实际上标记了要在第一遍中删除的元素,然后在第二遍中删除它们(移动其余元素)。在这种情况下可以进行优化:每次需要移除剩余元素时,我们只会在知道需要删除的所有元素时执行一次。
结论:
removeIf
。remove
应该用于删除单个已知元素。基准测试结果:
Benchmark Mode Cnt Score Error Units
RemoveTest.removeIfArrayList avgt 30 4,478 ± 0,194 ms/op
RemoveTest.removeIfLinkedList avgt 30 3,634 ± 0,184 ms/op
RemoveTest.removeIteratorArrayList avgt 30 27197,046 ± 536,584 ms/op
RemoveTest.removeIteratorLinkedList avgt 30 3,601 ± 0,195 ms/op
基准:
@Warmup(iterations = 5, time = 1000, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 10, time = 1000, timeUnit = TimeUnit.MILLISECONDS)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Fork(3)
@State(Scope.Benchmark)
public class RemoveTest {
private static final int NUMBER_OF_LIST_INDEXES = 1_000_000;
private static final String[] words = "Testing Lamba expressions with this String array".split(" ");
private ArrayList<String> arrayList;
private LinkedList<String> linkedList;
@Setup(Level.Iteration)
public void setUp() {
arrayList = new ArrayList<>();
linkedList = new LinkedList<>();
for (int i = 0 ; i < NUMBER_OF_LIST_INDEXES ; i++){
arrayList.add(words[i%6]);
linkedList.add(words[i%6]);
}
}
@Benchmark
public void removeIfArrayList() {
arrayList.removeIf(x -> x.contains("s"));
}
@Benchmark
public void removeIfLinkedList() {
linkedList.removeIf(x -> x.contains("s"));
}
@Benchmark
public void removeIteratorArrayList() {
for (ListIterator<String> it = arrayList.listIterator(arrayList.size()); it.hasPrevious();){
if (it.previous().contains("s")) it.remove();
}
}
@Benchmark
public void removeIteratorLinkedList() {
for (ListIterator<String> it = linkedList.listIterator(linkedList.size()); it.hasPrevious();){
if (it.previous().contains("s")) it.remove();
}
}
public static void main(String[] args) throws Exception {
Main.main(args);
}
}
答案 2 :(得分:1)
我认为您所看到的性能差异可能更多是由于squirrel <- read.table("F:/Stats ass 5/squirrel.data", header=TRUE, quote="\"")
View(squirrel)
squirrel$age=as.factor(squirrel$age)
squirrel$sex=as.factor(squirrel$sex)
squirrel$weight=as.factor(squirrel$weight)
squirrel$survive=as.numeric(squirrel$survive)
squirrel$number=as.numeric(squirrel$number)
在内部使用迭代器而在for循环中使用get和remove。这个PAQ中的答案有一些关于迭代器好处的好信息。
bayou.io的回答是正确的,您可以看到code for removeIf here它会进行两次通过以避免一遍又一遍地移动剩余的元素。