One is correct, the other isn’t.
du -sh *
du -sh -- * to avoid problems with filenames starting with
relies on the shell to expand the glob
du sees all the non-hidden files and directories in the current directory as individual arguments. This handles special characters correctly.
ls | xargs du -sh
xargs to process
xargs splits its input on whitespace (at least space, tab and newline, more with some implementations), also understanding some form of quoting, and runs
du (one (even for an empty input¹) or more invocations) with every single whitespace-separated string as individual arguments.
Both appear equivalent if your current directory doesn’t contain files with whitespace, single quote, double quote or backslash characters in their names, and if there are few enough files (but at least one) that
xargs runs only one
du invocation, but they’re not.
In terms of efficiency,
du -sh * uses one process,
ls | xargs du -sh uses at least three. There is one scenario where the pipe approach will work, while the glob won’t: if you have too many files in the current directory, the shell won’t be able to run
du with all their names in one go, but
xargs will run
du as many times as necessary to cover all the files, in which case you would see several lines, and files with more than one hard link may be counted several times.
See also Why *not* parse `ls`?
¹ If there's no non-hidden file in the current directory
du -sh -- * will either fail with an error by your shell, or with some shells like
du with a literal
* as argument and
du will complain about that
* file not existing.
ls | xargs du -sh --, most
xargs implementations (exceptions being some BSD) will run
du with no argument and so give the disk usage of the current directory (so also including the disk usage of the directory file itself and all hidden files and directories in it)