我如何重构这个循环?

时间:2023-01-17 04:14:31

I've got an application where I use primitive arrays and Lists for a class called Item. These are used interchangeably for legacy reasons (I also wish this was just one type, but it's the way it is).

我有一个应用程序,我使用原始数组和列表称为Item。由于遗留原因,这些可以互换使用(我也希望这只是一种类型,但它就是这样)。

Now I have to add a new method like this that works via a for-each loop:

现在我必须添加一个像这样的新方法,通过for-each循环:

public void something(Item... items) {
    for (Item i : items) {
        doStuff();
    }
}

public void something(List<Item> items) {
    for (Item i : items) {
        doStuff();
    }
}

In other words, exactly the same method twice for both primitive Arrays and Lists. Is there any way to nicely refactor this into a single method?

换句话说,对于原始数组和列表,两次完全相同的方法。有没有办法很好地将它重构为一个方法?

4 个解决方案

#1


68  

You can't shouldn't (*) do this in a single method. Item[] and List<Item> are unrelated types.

你不应该(*)在一个方法中这样做。 Item []和List 是不相关的类型。

You should make one of the overloads call the other: either something(Item... items) calls something(List<Item>), or something(List<Item>) calls something(Item... items).

你应该让其中一个重载调用另一个:要么(Item ... items)调用某个东西(List ),要么调用某个东西(List )调用某个东西(Item ... items)。

Of the two options, it is better for the array overload to call the list overload:

在这两个选项中,最好是数组重载调用列表重载:

public void something(Item... items) {
  something(Arrays.asList(item));
}

This is cheap, because it doesn't copy the array, but rather wraps it: creating the List is O(1).

这很便宜,因为它不会复制数组,而是包装它:创建List是O(1)。

If you were to invoke the array overload from the list overload:

如果要从列表重载调用数组重载:

public void something(List<Item> items) {
  something(items.toArray(new Item[0]));
}

This would be more expensive, since the toArray call has to create and populate an array: it is an O(n) operation, where n is the size of the list. However, it has the slight advantage that something would not be able to replace the contents of the List, since any updates to the array are simply discarded after execution.

这会更昂贵,因为toArray调用必须创建并填充数组:它是一个O(n)操作,其中n是列表的大小。但是,它有一些优点,即某些内容无法替换List的内容,因为对数组的任何更新都会在执行后被丢弃。


(*) You can, but it would be really gross, and not type-safe, as you'd have to accept an Object parameter, as there is no other common super type of List<Item> and Item[]; and you'd still end up having to repeat the loops for the two types; and you'd have to handle the possibility of a completely unrelated type being passed in (at runtime):

(*)你可以,但它确实很粗糙,而且不是类型安全的,因为你必须接受一个Object参数,因为没有其他常见的超类型List 和Item [];而你最终还是要为这两种类型重复循环;并且您必须处理传递完全不相关类型的可能性(在运行时):

public void something(Object obj) {
  if (obj instanceof List) {
    for (Object element : (List<?>) obj) {
      Item item = (Item) element;  // Potential ClassCastException.
      doStuff();
    }
  } else if (obj instanceof Item[]) {
    for (Item item : (Item[]) obj) {
      doStuff();
    }
  } else {
    throw new IllegalArgumentException();
  }
}

What a mess. Thank the maker for overloads.

真是一团糟。感谢制造商过载。

#2


16  

If you use Java 8 you could also just call forEach or map on your Stream and you are done, e.g.

如果你使用Java 8,你也可以在你的Stream上调用forEach或map,你就完成了,例如

yourStream.forEach(doStuff());

where doStuff() is the consumer dealing with the String or use yourStream.forEach(s -> doStuff()) if you don't want to handle the string and just do stuff.

doStuff()是消费者处理String或使用yourStream.forEach(s - > doStuff())如果你不想处理字符串而只是做东西。

You can obtain a stream as follows:

您可以按如下方式获取流:

Stream.of(yourArray) // or Arrays.stream(yourArray)
      .forEach(doStuff());

and for your list:

并为您的清单:

list.stream()
    .forEach(doStuff());

The main benefit of using the streams is probably readability. It may lose regarding performance and it may also lose if you don't want to call Stream.of/Arrays.stream or Collection.stream() just to obtain the stream.

使用流的主要好处可能是可读性。它可能会失去性能,如果您不想调用Stream.of / Arrays.stream或Collection.stream()来获取流,它也可能会丢失。

If you really want to keep the something(...) method (being able to deal with both: the varargs and the list) you are still requiring an overloaded method or use Andy Turner's proposal with the Object-parameter-method.

如果你真的想保留某些东西(...)方法(能够同时处理varargs和列表),你仍然需要重载方法或者使用Andy Turner的提议和Object-parameter-method。

#3


1  

You can implement a single method, in this case, the second one because it has a list as parameter. Instead of the first method, you can convert the array in a list with Arrays.asList(items) and then, you can call the first method. So, in the end, you will have only a single method (that has a list as parameter).

您可以实现单个方法,在本例中为第二个方法,因为它有一个列表作为参数。您可以使用Arrays.asList(items)转换列表中的数组,而不是第一种方法,然后,您可以调用第一个方法。因此,最后,您将只有一个方法(列表作为参数)。

Also, if the items list has few elements, you can use the lambda expressions from Java 8:

此外,如果items列表中的元素很少,则可以使用Java 8中的lambda表达式:

items.foreach(item -> doStuff(item));

So, you will not have a method that contains only one loop and the code will be easier to read.

因此,您将不会有一个只包含一个循环的方法,代码将更容易阅读。

#4


0  

You should achieve this by passing List after converting it to array.

您应该通过在将其转换为数组后传递List来实现此目的。

Retain this as your single method,

保留这个作为你的单一方法,

public void something(Item... items) {
   for (Item i : items) {
       doStuff();
   }
}

and when you want to pass a List<Item> then pass like this,

当你想传递一个List 然后像这样传递,

something(listItem.toArray(new Item[listItem.size()]))

something(listItem.toArray(new Item [listItem.size()]))

#1


68  

You can't shouldn't (*) do this in a single method. Item[] and List<Item> are unrelated types.

你不应该(*)在一个方法中这样做。 Item []和List 是不相关的类型。

You should make one of the overloads call the other: either something(Item... items) calls something(List<Item>), or something(List<Item>) calls something(Item... items).

你应该让其中一个重载调用另一个:要么(Item ... items)调用某个东西(List ),要么调用某个东西(List )调用某个东西(Item ... items)。

Of the two options, it is better for the array overload to call the list overload:

在这两个选项中,最好是数组重载调用列表重载:

public void something(Item... items) {
  something(Arrays.asList(item));
}

This is cheap, because it doesn't copy the array, but rather wraps it: creating the List is O(1).

这很便宜,因为它不会复制数组,而是包装它:创建List是O(1)。

If you were to invoke the array overload from the list overload:

如果要从列表重载调用数组重载:

public void something(List<Item> items) {
  something(items.toArray(new Item[0]));
}

This would be more expensive, since the toArray call has to create and populate an array: it is an O(n) operation, where n is the size of the list. However, it has the slight advantage that something would not be able to replace the contents of the List, since any updates to the array are simply discarded after execution.

这会更昂贵,因为toArray调用必须创建并填充数组:它是一个O(n)操作,其中n是列表的大小。但是,它有一些优点,即某些内容无法替换List的内容,因为对数组的任何更新都会在执行后被丢弃。


(*) You can, but it would be really gross, and not type-safe, as you'd have to accept an Object parameter, as there is no other common super type of List<Item> and Item[]; and you'd still end up having to repeat the loops for the two types; and you'd have to handle the possibility of a completely unrelated type being passed in (at runtime):

(*)你可以,但它确实很粗糙,而且不是类型安全的,因为你必须接受一个Object参数,因为没有其他常见的超类型List 和Item [];而你最终还是要为这两种类型重复循环;并且您必须处理传递完全不相关类型的可能性(在运行时):

public void something(Object obj) {
  if (obj instanceof List) {
    for (Object element : (List<?>) obj) {
      Item item = (Item) element;  // Potential ClassCastException.
      doStuff();
    }
  } else if (obj instanceof Item[]) {
    for (Item item : (Item[]) obj) {
      doStuff();
    }
  } else {
    throw new IllegalArgumentException();
  }
}

What a mess. Thank the maker for overloads.

真是一团糟。感谢制造商过载。

#2


16  

If you use Java 8 you could also just call forEach or map on your Stream and you are done, e.g.

如果你使用Java 8,你也可以在你的Stream上调用forEach或map,你就完成了,例如

yourStream.forEach(doStuff());

where doStuff() is the consumer dealing with the String or use yourStream.forEach(s -> doStuff()) if you don't want to handle the string and just do stuff.

doStuff()是消费者处理String或使用yourStream.forEach(s - > doStuff())如果你不想处理字符串而只是做东西。

You can obtain a stream as follows:

您可以按如下方式获取流:

Stream.of(yourArray) // or Arrays.stream(yourArray)
      .forEach(doStuff());

and for your list:

并为您的清单:

list.stream()
    .forEach(doStuff());

The main benefit of using the streams is probably readability. It may lose regarding performance and it may also lose if you don't want to call Stream.of/Arrays.stream or Collection.stream() just to obtain the stream.

使用流的主要好处可能是可读性。它可能会失去性能,如果您不想调用Stream.of / Arrays.stream或Collection.stream()来获取流,它也可能会丢失。

If you really want to keep the something(...) method (being able to deal with both: the varargs and the list) you are still requiring an overloaded method or use Andy Turner's proposal with the Object-parameter-method.

如果你真的想保留某些东西(...)方法(能够同时处理varargs和列表),你仍然需要重载方法或者使用Andy Turner的提议和Object-parameter-method。

#3


1  

You can implement a single method, in this case, the second one because it has a list as parameter. Instead of the first method, you can convert the array in a list with Arrays.asList(items) and then, you can call the first method. So, in the end, you will have only a single method (that has a list as parameter).

您可以实现单个方法,在本例中为第二个方法,因为它有一个列表作为参数。您可以使用Arrays.asList(items)转换列表中的数组,而不是第一种方法,然后,您可以调用第一个方法。因此,最后,您将只有一个方法(列表作为参数)。

Also, if the items list has few elements, you can use the lambda expressions from Java 8:

此外,如果items列表中的元素很少,则可以使用Java 8中的lambda表达式:

items.foreach(item -> doStuff(item));

So, you will not have a method that contains only one loop and the code will be easier to read.

因此,您将不会有一个只包含一个循环的方法,代码将更容易阅读。

#4


0  

You should achieve this by passing List after converting it to array.

您应该通过在将其转换为数组后传递List来实现此目的。

Retain this as your single method,

保留这个作为你的单一方法,

public void something(Item... items) {
   for (Item i : items) {
       doStuff();
   }
}

and when you want to pass a List<Item> then pass like this,

当你想传递一个List 然后像这样传递,

something(listItem.toArray(new Item[listItem.size()]))

something(listItem.toArray(new Item [listItem.size()]))